Detailed explanation of C++ polymorphism (virtual function rewriting, interface inheritance, detailed explanation of virtual function table)

Table of contents

1. The concept of polymorphism

2. Definition and implementation of polymorphism

  2.1 Conditions for polymorphism

  2.2 Virtual function rewriting

  2.3 C++11 override and final

  2.4 Comparison of overloading, overriding (overriding), hiding (redefining)

3. Abstract class

  3.1 Concept

  3.2 Interface inheritance and implementation inheritance

4. The principle of polymorphism

  4.1 Virtual function table

  4.2 The principle of polymorphism

  4.3 Dynamic binding and static binding

5. Virtual function table of single inheritance and multiple inheritance relationship

  5.1 Virtual function table in single inheritance

  5.2 Virtual function table in multiple inheritance

6. Common interview questions and answers


1. The concept of polymorphism

Polymorphism: many forms. Specifically, it is to complete a certain behavior. When different objects are completed, different states will be generated.

The act of buying tickets. Ordinary people buy tickets at full price; students buy tickets at half price; soldiers buy tickets at priority.

Here, ordinary people, students, and soldiers (different objects) have different states to complete the ticket purchase action.

And at the same time, most people should have used it. For new users, it will definitely be able to cut 0.1 yuan successfully; for old users, it may fail or add several fragments (quite a lot of fragments are combined into 0.1 yuan).

For users of different levels, the status of cutting a knife is also different.

Alipay scans the code to receive red envelopes. Some people can get 8 yuan or 10 yuan by scanning the red envelope...while some people can only get a few cents by scanning the red envelope... Behind this is actually a polymorphic behavior. (PS: Made up, for entertainment only) Alipay will first analyze your account data, for example, if you are a new user / do not pay with Alipay frequently, then you need to be encouraged to use Alipay, then your scan code amount = random() %99; For example, if you often use Alipay to pay/Alipay account has no money all year round, then you don’t need to be too encouraged to use Alipay, then your scan code amount = random()%1; the same scan code action, different users Scanning the code to get different red envelopes is also a polymorphic behavior.

2. Definition and implementation of polymorphism

  2.1 Conditions for polymorphism

Polymorphism refers to class objects in different inheritance relationships, to call the same function, resulting in different behaviors.

To form a polymorphic condition in inheritance :

  • The virtual function must be called through the pointer or reference of the base class
  • The called function must be a virtual function, and the derived class must override the virtual function of the base class

  2.2 Virtual function rewriting

Virtual function rewriting/overwriting conditions: virtual function (only member functions can add virtual) + triple (function name, parameters, return value)

Does not meet the rewrite, is to hide the relationship.

class Person {
public:
 virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
 virtual void BuyTicket() { cout << "买票-半价" << endl; }
};

 Exceptions to virtual function rewriting (pit dug by C++)

  • Subclass virtual functions do not add virtual, which still constitutes rewriting. (First inherit the virtual function, then rewrite (implement))

  •  Covariant (base class and derived class virtual function return value type is different)

When the derived class overrides the virtual function of the base class, the return value type of the virtual function of the base class is different. That is, the base class virtual function returns a pointer or reference to the base class object, and the derived class virtual function returns a pointer or reference to the derived class object, which is called covariance.

  •  Rewriting of destructors (base class and derived class destructors have different names)
If the destructor of the base class is a virtual function, as long as the destructor of the derived class is defined at this time, no matter whether the virtual keyword is added, it will be rewritten with the destructor of the base class, although the name of the destructor of the base class and the derived class different. Although the function names are different, it seems to violate the rewriting rules, but it is not. Here it can be understood that the compiler has made special treatment for the name of the destructor. After compilation, the name of the destructor is uniformly processed as destructor . (It is recommended to define it as a virtual function)

  2.3 C++11 override and final

C++ has stricter requirements on function rewriting, but in some cases, due to negligence, it may cause the function name to be written in alphabetical order but cannot constitute overloading, and this error will not be reported during compilation, only when the program is running It is not worth the loss to debug without getting the expected results, so:
C++11 provides two keywords, override and fifinal, which can help users detect whether to rewrite.
  • final: Modifies the virtual function, indicating that the virtual function can no longer be rewritten 
class Car
{
public:
 virtual void Drive() final {}
};
class BMW :public Car
{
public:
 virtual void Drive() {cout << "BMW-舒适" << endl;}
};
  •  override: Check whether the virtual function of the derived class overrides a virtual function of the base class, and if not, compile and report an error
class Car{
public:
 virtual void Drive(){}
};
class BMW :public Car {
public:
 virtual void Drive() override {cout << "BMW-舒适" << endl;}
};

  2.4 Comparison of overloading, overriding (overriding), hiding (redefining)

3. Abstract class

  3.1 Concept

Write =0 after the virtual function , then this function is a pure virtual function . A class that contains pure virtual functions is called an abstract class (also called an interface
class), abstract classes cannot instantiate objects . After the derived class is inherited, the object cannot be instantiated, only to rewrite the pure virtual function, and the derived class
Classes can instantiate objects . The pure virtual function specifies that the derived class must be rewritten , and the pure virtual function also reflects the interface inheritance .
class Car
{
public:
virtual void Drive() = 0;
};

  3.2 Interface inheritance and implementation inheritance

  • The inheritance of ordinary functions is a kind of implementation inheritance . The derived class inherits the functions of the base class, and the functions can be used. What is inherited is the actuality of the function.
now.
  • The inheritance of virtual functions is a kind of interface inheritance . The derived class inherits the interface of the virtual function of the base class. The purpose is to rewrite and achieve
Polymorphism inherits interfaces. So if you don't implement polymorphism, don't define functions as virtual functions.

Let's do a road interview question and look at interface inheritance:

   class A
   {
   public:
       virtual void func(int val = 1){ std::cout<<"A->"<< val <<std:endl;}
       virtual void test(){ func();}
   };
   
   class B : public A
   {
   public:
       void func(int val=0){ std::cout<<"B->"<< val <<std::endl; }
   };
   
   int main(int argc ,char* argv[])
   {
       B*p = new B;
       p->test();
       return 0;
   }

A: A->0 B: B->1 C: A->1 D: B->0 E: Compilation error F: None of the above is correct 

Answer: B

Analysis: Virtual function rewriting is interface inheritance, rewriting implementation, and inheriting interface.

In the above example, the interface inherits the above void func(int val=1). Call test, B* is passed to A* (slicing operation) but this still points to the B object, this-> calls the function pointing to the object. (polymorphic)

Polymorphism points to who calls whom! 

4. The principle of polymorphism

  4.1 Virtual function table

The essence of the virtual function table is an array of function pointers

// 这里常考一道笔试题:sizeof(Base)是多少?
class Base
{
public:
 virtual void Func1()
 {
 cout << "Func1()" << endl;
 }
private:
 int _b = 1;
};

 Through observation and testing, we found that the b object is 8 bytes. In addition to the _b member, there is an additional __vfptr placed in front of the object (VS platform). The pointer in the object (Vs x86, pointer 4bytes) is called a virtual function table pointer. A class with a virtual function table has at least one virtual function table pointer, because the address of the virtual function must be placed in the virtual function table , and the virtual function table is also called a virtual table.

Note: Where does the virtual function exist? Where does the virtual table exist? 

The virtual table stores virtual function pointers, not virtual functions . Virtual functions are the same as ordinary functions, and they all exist in the code segment, but their pointers are stored in the virtual table. In addition, what is stored in the object is not a virtual table, but a virtual table pointer. The test under VS found that there is a code segment in the virtual table .

  4.2 The principle of polymorphism

Give a model to demonstrate:

class Person {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }

	virtual void Func() { cout << "Func" << endl; }


	int _a = 0;
};

class Student : public Person {
public:
	virtual void BuyTicket() { cout << "买票-半价" << endl; }

	int _b = 0;
};

void Func(Person& p)
{
	p.BuyTicket();
}

int main()
{
	Person Mike;
	Func(Mike);

	Student Johnson;
	Func(Johnson);

	return 0;
}

Summarize:

The essential principle of polymorphism meets the two conditions of polymorphism. Then when calling, it will find the corresponding virtual function address in the virtual table pointing to the object, and call it.

Polymorphism (when the program is running, find the function address in the virtual table pointing to the object and call it, so p points to who calls whose virtual function)

Ordinary function call, determine the address of the function when compiling and linking, and call it directly when running. The type is who calls whom.

  4.3 Dynamic binding and static binding

  • Static binding, also known as early binding (early binding), determines the behavior of the program during program compilation , also known as static polymorphism , such as: function overloading
  • Dynamic binding, also known as late binding (late binding), is to determine the specific behavior of the program according to the specific type obtained during the running of the program, and call specific functions, also known as dynamic polymorphism .

5. Virtual function table of single inheritance and multiple inheritance relationship

  5.1 Virtual function table in single inheritance

 in conclusion:

  • Single inheritance: Objects of the same class share a virtual table
  • Under VS, no matter whether the rewriting is completed or not, the subclass virtual table is not the same as the parent class virtual table (the content changes)
  • The subclass virtual table has its own virtual function and the parent class virtual function (the virtual function pointer is stored)

 Test whether the virtual function of the subclass is placed in the virtual table of the subclass or the virtual table of the parent class:

class Base { 
public :
 virtual void func1() { cout<<"Base::func1" <<endl;}
 virtual void func2() {cout<<"Base::func2" <<endl;}
private :
 int a;
};
class Derive :public Base { 
public :
 virtual void func1() {cout<<"Derive::func1" <<endl;}
 virtual void func3() {cout<<"Derive::func3" <<endl;}
 virtual void func4() {cout<<"Derive::func4" <<endl;}
private :
 int b;
};

 Hey, it’s amazing, the func3 and func4 of the subclasses are missing here, are they really missing? Actually not! Here is that the monitoring window of the vs compiler deliberately hides these two functions, which can be considered a bug. So how do we view the virtual table of d? We use the following code to print out the functions in the virtual table.

typedef void(*VFPTR)();
void PrintVFTable(VFPTR* table)
{
	for (size_t i = 0; table[i] != nullptr; ++i)
	{
		printf("vft[%d]:%p->", i, table[i]);
		VFPTR pf = table[i];
		pf();
	}
	cout << endl;
}

 As a result, we can see that the virtual function of the subclass is indeed placed in the subclass's own virtual table.

  5.2 Virtual function table in multiple inheritance

in conclusion:

  • Non-overridden virtual functions of multiple inheritance derived classes are placed in the virtual function table of the first inherited base class part
class Base1 {
public:
 virtual void func1() {cout << "Base1::func1" << endl;}
 virtual void func2() {cout << "Base1::func2" << endl;}
private:
 int b1;
};
class Base2 {
public:
 virtual void func1() {cout << "Base2::func1" << endl;}
 virtual void func2() {cout << "Base2::func2" << endl;}
private:
 int b2;
};
class Derive : public Base1, public Base2 {
public:
 virtual void func1() {cout << "Derive::func1" << endl;}
 virtual void func3() {cout << "Derive::func3" << endl;}
private:
 int d1;
};
int main()
{
 Derive d;
 return 0;
}

The virtual function of subclass self-increment cannot be seen in the VS monitoring window.

Test the result through the function code written above:


Diamond Inheritance and Rhombus Virtual Inheritance posted 2 articles by Mr. Chen Hao: C++ Virtual Function Table Analysis | Cool Shell - CoolShell Memory Layout of C++ Objects | Cool Shell - CoolShell


6. Common interview questions and answers

  • Can an inline function be a virtual function?
Answer: Yes, but the compiler ignores the inline attribute, and this function is no longer inline, because the virtual function must be placed in the virtual table.
  • Can a static member be a virtual function?
Answer: No, because the static member function does not have a this pointer, and the virtual function table cannot be accessed by calling the type:: member function, so the static member function cannot be placed in the virtual function table.
  • Can a constructor be virtual?
Answer: No, because the virtual function table pointer in the object is initialized at the constructor initialization list stage.
  • Can a destructor be virtual? Under what circumstances is the destructor a virtual function?
Answer: Yes, and it is best to define the destructor of the base class as a virtual function.
  • Is object access to ordinary functions faster or virtual functions faster?
Answer: First of all, if it is an ordinary object, it is as fast. If it is a pointer object or a reference object, the ordinary function called is fast, because it constitutes polymorphism, and calling a virtual function at runtime needs to look up in the virtual function table.
  • At what stage is the virtual function table generated and where does it exist?
Answer: The virtual function table is generated during the compilation stage, and generally exists in the code segment (constant area).
  • What is an abstract class? The role of abstract classes?
Answer: Function: The abstract class forcibly rewrites the virtual function, and the abstract class reflects the interface inheritance relationship.
  • Can copy construction and operator= be virtual functions?

Answer: Copy construction is not possible, copy construction is also a constructor. operator= works, but has no real value.

 

Guess you like

Origin blog.csdn.net/bang___bang_/article/details/130851285