C++【In-depth understanding of polymorphism】

1. The concept and implementation of polymorphism

(1) The concept of polymorphism

In layman's terms, it is a variety of forms. Simply put, it is to complete a certain behavior. When different objects are completed, different states will be produced.
Specifically, the address of a derived class object can be assigned to a base class pointer. For a statement that calls a virtual function with the same name and the same parameter list in both the base class and the derived class through the base class pointer or reference, it is not sure whether the virtual function of the base class or the derived class is to be executed when compiling; and when the program runs When this statement is reached, if the base class pointer points to a base class object, the base class virtual function is called; if the base class pointer points to a derived class object, the derived class virtual function is called. This mechanism is called polymorphism.

Let's first look at a polymorphic scenario:
first look at the following figure: the result of this operation is no problem, first call the subclass destructor, in the subclass, after the subclass destructor is completed, the parent class destructor will be called automatically; finally Parent class destructor. Those defined later are destructed first.
insert image description here
Then we change the scene again:
I have a parent class pointer, which can point to the parent class object or the subclass object, but the destructor is not adjusted correctly, pointing to the parent class object calls the parent class destructor, pointing to the The destructor of the class still calls the parent class. If there are resources to be released in the subclass, if it is not called, it will cause a memory leak.
Reason: delete consists of two parts, p1->destructor(), operator delete(p1); p2->destructor, operator delete(p2), so the destructors of the parent class and the subclass form a hidden relationship. If there is no polymorphism, we can call whoever our type is, and call the parent class if the type is a pointer to the parent class. This is no problem, but here we don’t expect it to be called by type, we expect it to be called by the object of the pointer : Point to the parent class object to invoke the destruction of the parent class, and point to the subclass object to invoke the destruction of the subclass.
insert image description here
Why the first part of the function name is the same, it is to prepare for the polymorphic scene, as long as they are the same, the destructor function can be called uniformly, how can we add a virtual function, as shown in the figure below:
insert image description here

(2) How to form polymorphism

Polymorphism is when class objects with different inheritance relationships call the same function, resulting in different behaviors. For example, child inherits from Person. The Person object buys clothes in large sizes, and the chlid object buys clothes in small sizes.
There are two conditions to constitute polymorphism in inheritance :
1. The called function must be a virtual function, and the derived class must rewrite the virtual function of the base class (function name, parameters, return value). Different default parameters also constitute triple, which refers to the type.
2. The virtual function must be called through the pointer or reference of the base class.
Virtual function: the class member function modified by virtual is called virtual function

class Person {
    
    
public:
	virtual void BuyClothes() {
    
     cout << "买衣服-大码" << endl; }
};
class child : public Person {
    
    
public:
	virtual void BuyClothes() {
    
     cout << "买衣服-小码" << endl; }
};
int main()
{
    
    
	Person p;
	child c;
	Person& p1 = p;
	Person& c2 = c;
	p.BuyClothes();
	c.BuyClothes();
	return 0;
}

insert image description here
1. If it does not satisfy polymorphism, it depends on the type, which means to see the type of the caller, call the member function of this type, whether it is a pointer or a reference, adjust the parent if it is a parent class, and drop the derivation if it is a derived class.
2. If polymorphism is satisfied, it depends on the object being called and the object pointed to.
3. If the parent class has virtual and the subclass has virtual, it is not polymorphic, it is hidden;

(3) 2 exceptions for virtual function rewriting

The first one: Subclasses do not need to write virtual.
If the parent class has virtual and the subclass does not have virtual, it is also polymorphic. This is not in conformity with the three rules. It is like this. If the parent class does not write virtual, it must not be a virtual function. When the parent class is a virtual function, the subclass does not add virtual, but it rewrites the virtual function. The writing reflects the interface inheritance (function declaration), which virtual void BuyClothes() inherits it, inherits the interface of the parent class, and rewrites the implementation of the parent class function, so it is considered a virtual function without adding virtual, because the parent class is a virtual function, put it inherited.
The second one: covariance, the return value can be different premise parent-child relationship pointer or
reference The value types are 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. As shown in the picture:
insert image description here

(4) Classic analysis to consolidate knowledge points

See the following questions:

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

The func in A and B is composed of rewriting, which satisfies the three-same, and because it contains default parameters, and the three-tong is about the type, which is also a rewrite. As mentioned earlier, the base class adds virtual, and the subclass does not matter whether it is added or not. It is also a virtual function, because it inherits the parent class interface, and the rewritten implementation satisfies the first condition of polymorphism ; because B inherits A, p calls the test in A, whoever calls it is this, and its type is A* This, although it is inherited, will not change the parameters of the function, which is equivalent to p being a subclass pointer, that is, the subclass object is passed to a parent class pointer, which satisfies the second condition of polymorphism. If polymorphism is satisfied, whoever points to calls whoever points to the subclass and calls the member function of the subclass.
To sum up, among the member functions of subclasses, func is a virtual function, what is rewritten is the implementation, what inherits is the interface, and inherits the default value at the same time, the final result is: B=1;

(5) override 和 final

C++ has strict requirements for function rewriting, but in some cases it may be negligent, which may cause the function not to be rewritten, for example, the parent class forgot to add virtual, so: C++11 provides two keys: override and final Words, which can help users detect rewriting.
1. final: Modifies the virtual function, indicating that the virtual function can no longer be rewritten. If you make a virtual function and don't want to be rewritten, use final.

class P
{
    
      public: virtual void sleep() final {
    
    }};
class s :public P
{
    
      public: virtual void sleep() {
    
    cout << "睡觉" << endl;}};

2.override: Check whether the virtual function of the subclass overrides a virtual function of the parent class, and if not, compile and report an error. Essentially this keyword lets us force rewrites.

class P
{
    
    public:virtual void sleep(){
    
    }};
class S :public P
{
    
    public:virtual void sleep() override {
    
    cout << "睡觉" << endl;}};

(6) Summary

The inheritance of ordinary functions is a kind of overall inheritance. The subclass inherits the functions of the parent class, can use the function, and inherits the implementation of the function. The inheritance of virtual functions is a kind of interface inheritance. The subclass inherits the interface of the virtual function of the parent class. The purpose is to rewrite and form polymorphism. What is inherited is the interface. If you don't implement polymorphism, don't define functions as virtual functions.
Virtual functions are born for rewriting, and rewriting is born for polymorphism.

Second, the principle of polymorphism

(1) Know the virtual function table

First observe the size of the class with virtual functions:
insert image description here
why it is 8 bytes, not 4 bytes, because there is an extra pointer __vfpt in the head of the object, this pointer is called the virtual function table pointer, v stands for virtual, and f stands for function. A class containing virtual functions 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 referred to as the virtual table. But what is placed in this table in the derived class.
As shown in the figure:
insert image description here
look at the following code:

class Person {
    
    
public:
	virtual void BuyClothes() {
    
     cout << "买衣服-大码" << endl; }


};
class child : public Person {
    
    
public:
	virtual void BuyClothes() {
    
     cout << "买衣服-小码" << endl;  }
};
int main()
{
    
    
	Person p;
	child c;
	Person& p1 = p;
	Person& c2 = c;
	p1.BuyClothes();
	c2.BuyClothes();
	return 0;
}

insert image description here
Observing the above, we can see that when p1 points to the p object, p->BuyClothes finds the virtual function Person::BuyClothes in the virtual table of p. When c2 points to the c object, c2->BuyClothes finds the virtual function child::BuyClothes in the virtual table of c.

(2) In-depth understanding of principles

Analysis : How did the above do it? We can see that the virtual function of the parent class is stored in the virtual table of the parent class object, and the virtual function of the subclass is stored in the virtual table of the subclass object. The compiler also determines whether the structure constitutes polymorphism:
if it does not constitute polymorphism , the call address is directly determined at compile time, and it has nothing to do with who it points to. If it is a pointer or reference of the parent class, it will call the function of the parent class and go to the parent class. Find this function in the class and determine the address. Simply speaking, it is an ordinary call, which is related to the type;
if it constitutes polymorphism , the compiler does not know who is calling, and first generates its own virtual function table. To find the pointed virtual table is to look at the pointed object. If you point to the parent class, you will find the virtual function of the parent class in the parent class object. If you point to the subclass, you will cut it and find the virtual function of the subclass.
Moreover, the Person reference or pointer we use does not know whether it is pointing to the parent class or the subclass. Pass the parent class object to me, and look at the virtual table of the parent class object. If you find this virtual function, if the subclass object passed For me, I will cut it here. In fact, what I see is also a parent class object, but this is not a pure parent class object, but the part of the parent class in the subclass object. The p1 and c2 calls are the same function , the final result of the call is different, because different objects are passed, and the vtables of different objects have their own virtual functions, pointing to the parent class to call the parent class, pointing to the subclass to call the subclass, and these two lines of code are insensitive. What you see is a parent class object, but it is the direct parent class object, or the parent class object in the subclass object.
Reanalysis:
Rewriting also has a name called coverage. Rewriting is to inherit the interface of the parent class and rewrite its implementation. It is an upper concept at the grammatical level; The location of the virtual table corresponding to the class will copy the image and overwrite it with its own virtual function, which is a concept at the principle level.
Polymorphism is to run in the virtual table of the object to find, so whatever can be passed can be executed, because the table has already been generated, the parent class virtual table stores the parent class virtual function, and the child class virtual table stores the subclass virtual function. If it is passed to my parent class, I will go to the table to find the parent class virtual function. If it is passed to my child class, it will Looking for a subclass virtual function. is a designed implementation.
Let’s have a deeper understanding of coverage:
If you add another virtual function to the parent class in the above function, the result:
insert image description here
the first virtual function has been rewritten, and the subclass first copies the performance of the parent class, and the rewritten one will be overwritten. Own, not rewritten is not called coverage. It means that when the parent class function has a virtual function, first copy the content of the virtual table in the parent class to the subclass virtual table. If the subclass rewrites a virtual function in the parent class, the subclass uses its own The virtual function overrides the virtual function of the parent class in the virtual table, and the newly added virtual function of the subclass itself is added to the end of the subclass virtual table according to the order of declaration in the subclass.

(3) Troubleshooting

Why is the polymorphic condition rewritten?
Because only if it is rewritten, the virtual table of the subclass will overwrite the corresponding virtual function, because the position of the virtual function in the virtual table must be overwritten.
Why is it a pointer or a reference?
Because a pointer reference can point to both a parent class object and a subclass object. Even if it points to a subclass, what you see is a parent class, but it is cut in the subclass.
Why not store the virtual function address directly in front of the object header?
Because there may be multiple virtual functions in an object, and the virtual table of the same type is the same. The virtual function table is an array of virtual function pointers, which has been determined at compile time. alright. Polymorphism is to find.

(4) Why can't objects achieve polymorphism?

If it is a pointer or a reference, point to the parent class or refer to the parent class, if it is a pointer or reference, when pointing to the subclass, cut out the part of the parent class of the subclass object to point to that part, and what you see is still in the subclass The part of that part, the virtual table of that part is still a subclass.
insert image description here

If it is an object, there is no problem if it is a parent class. If it is a subclass, the subclass slices the parent class, and the members will be copied. If the virtual table is not copied, the virtual table in the parent class object will always be the parent class. Virtual function, except when passing a subclass object to c2, in addition to copying members, it also copies the virtual table of the subclass, but dare not copy the virtual table when the subclass is sliced, because the pointer or reference is cut to the part it points to Part or reference alias, but dare to copy to achieve polymorphism, but in fact dare not copy, copy will be messy, whether the virtual table of a parent class object is a virtual function of the parent class or a virtual function of the subclass Not sure, maybe this is a parent class object, pointing to the parent class, looking for the virtual table of the parent class, no problem, but it may also be an object of the parent class, copied or assigned by the sub class object, just It is completely unclear. If you give a parent class object, you will not be able to confirm who owns the virtual table in the parent class object, and if you use a pointer to call it when it is polymorphic, the pointer points to the parent class. It is also possible to call the subclass, because the parent class There is also a subclass virtual table in the object, which is unreasonable. My parent class object is a pure parent class object, and the parent class object must be a virtual table of the parent class.
If it is an object, only the members are copied but not the virtual table when slicing, as shown in the figure below, c is the subclass, and c2 is the parent class:
insert image description here

(5) Dynamic binding and static binding

1. 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, cin, cout.
2. Dynamic binding, also known as late binding (late binding), is to determine the specific behavior of the program according to the specific type of pointer obtained during the running of the program, and call specific functions, also known as dynamic polymorphism.

(6) supplement

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. What is stored in the object is not a virtual table, but a virtual table pointer. Then the verification of the virtual table storage will find that there is a code segment under vs. The code segment is compiled code to generate assembly-to-binary instructions. Virtual tables are generated during compilation. The pointer to the virtual table in the object is generated during the constructor initialization list phase at runtime.

3. Virtual function tables of single inheritance and multiple inheritance relationships

(1) How to print the virtual function table in single inheritance

class A {
    
    
public:
	virtual void func1() {
    
     cout << "A::func1" << endl; }
	virtual void func2() {
    
     cout << "A::func2" << endl; }
private:
	int a;
};
class B :public A{
    
    
public:
	virtual void func1() {
    
     cout << "B::func1" << endl; }
	virtual void func3() {
    
     cout << "B::func3" << endl; }
	virtual void func4() {
    
     cout << "B::func4" << endl; }
private:
	int b;
};
int main()
{
    
    
    A a;
	B b;
	return 0;
}

insert image description here
insert image description here

Observing the monitoring window, it is found that func3 and func4 of the derived class cannot be seen, and they are not entered into the virtual table of the derived class, but they can be seen from the memory. Here, the compiler's monitoring window deliberately hides these two functions, which can also be considered a bug. You can check the virtual table of d to confirm whether it is true or not.
We can use code to print out the functions in the vtable:

class A {
    
    
public:
	virtual void func1() {
    
     cout << "A::func1" << endl; }
	virtual void func2() {
    
     cout << "A::func2" << endl; }
private:
	int a;
};
class B :public A {
    
    
public:
	virtual void func1() {
    
     cout << "B::func1" << endl; }
	virtual void func3() {
    
     cout << "B::func3" << endl; }
	virtual void func4() {
    
     cout << "B::func4" << endl; }
private:
	int b;
};
typedef void(*VFPTR) ();
void PrintVTable(VFPTR vTable[])
{
    
    
	for (int i = 0; vTable[i] != nullptr; ++i)
	{
    
    
		printf(" [%d]:%p\n", i, vTable[i]);
		VFPTR f = vTable[i];
		VFPTR f = vTable[i];
		f();
	}
	cout << endl;
}
int main()
{
    
    
	A a;
	B b;
	VFPTR* vTableb = (VFPTR*)(*(int*)&a);
	PrintVTable(vTableb);
	VFPTR* vTabled = (VFPTR*)(*(int*)&b);
	PrintVTable(vTabled);
	return 0;
}

The above code: first typedef a function pointer, declare a function pointer, and the defined variable must be inside.
In the PtinfVFtable() function, VF_PTR[] is an array of function pointers. To print this array, use a for loop. Under VS, put an empty space behind the virtual function array, so it will stop when it encounters an empty space.
The key is that in the main function, the address of the virtual table should be taken out and then passed to it.
Two methods:
The first method is to take out the first 4 bytes of objects a and b, which are the pointers of the virtual table. The essence of the virtual function table is an array of pointers storing virtual function pointers. A nullptr is placed at the end of this array.
First take the address of b, and convert it into an int pointer; then dereference the value, and get the value of 4 bytes in the head of the b object, which is the pointer to the virtual table; then force it into VFPTR, because the virtual table is a storage An array of VFPTR type (virtual function pointer type); the virtual table pointer is passed to PrintVTable to print the virtual table. It should be noted that the code that prints the virtual table often crashes, because the compiler sometimes does not handle the virtual table cleanly, and the nullptr is not placed at the end of the virtual table, resulting in an out-of-bounds, which is a problem of the compiler. Just click - Generate - Clean Solution in the directory bar, and then compile.
The second type : just (VFPTR*)(*(int*)&a);replace it with (*(VF_PTR**)&a). Let me first say that if there is an array of int*, its first element address should be an int**, which is analogous to the virtual function table pointer array is VF_PTR*, but when passing it to the past, first turn to level 2, which is **, and then solve The reference becomes * so that the types match. Why not write it directly ((VF_PTR*)&a), because the address to be passed is in the first four bytes of the object, and it needs to be dereferenced to take four bytes indirectly. The first four bytes of the object are VF_PTR*, how to get it can only be dereferenced, And if it is changed to this way, the first address of the entire object is passed, which is not what we want.
The second type is more portable than the first type, because the int size may change when the platform is changed. The second type is adaptive although the pointer also changes.
Final running result:
insert image description here

(2) Virtual function table in multiple inheritance

(1) The problem of different virtual addresses encountered in the multi-inheritance virtual function table

First observe the following code and monitor window:

class A {
    
    
public:
	virtual void func1() {
    
     cout << " A::func1" << endl; }
	virtual void func2() {
    
     cout << " A::func2" << endl; }
private:
	int _a;
};
class B :public A {
    
    
public:
	virtual void func1() {
    
     cout << " B::func1" << endl; }
	virtual void func2() {
    
     cout << " B::func2" << endl; }
private:
	int _b;
};
class C :public A, public B
{
    
    
public:
	virtual void func1() {
    
     cout << " B::func1" << endl; }
	virtual void func3() {
    
     cout << " B::func3" << endl; }
private:
	int _c;
};
//typedef void(*VFPTR) ();
//void PrintVTable(VFPTR vTable[])
//{
    
    
//	for (int i = 0; vTable[i] != nullptr; ++i)
//	{
    
    
//		printf(" [%d]:%p:\n", i, vTable[i]);
//		VFPTR f = vTable[i];
//		f();
//	}
//	cout << endl;
//}
int main()
{
    
    
	C c;
//VFPTR* vTableb = (VFPTR*)(*(int*)&c);
//PrintVTable(vTableb);
//VFPTR* vTabled = (VFPTR*)(*(int*)((char*)&c+sizeof(A)));
//PrintVTable(vTabled);

	return 0;
}

insert image description here
Let’s not print the virtual table first. From the monitoring window, c has two virtual tables, because it inherits A and B at the same time, and rewrites func1, but where func3 is placed. At this time, you can first print two virtual tables:
how Printing:
The first virtual table is easy to print, mainly because it is the second longest. A and B are not consecutive in C. You need to skip A first and add the size of A bytes, because the address here is A*, and adding 1 is Add an A, here you need to force &c to char*, and add a byte. You can also use offset: B* b=&c;PrintVFTable((VF_PTR*)(*(int*)(b))), the pointer of the slice will be automatically offset. As shown in the figure, it is found that func3 is stored in the first virtual table :
insert image description here
We also found that after multiple inheritance, the address of func1 rewritten in the virtual table is different.
insert image description here

(2) The principle of different virtual addresses in multiple inheritance

Based on the phenomena observed above, think about why it should be different?

class A {
    
    
public:
	virtual void func1() {
    
     cout << "  A::func1" << endl; }
	virtual void func2() {
    
     cout << "  A::func2" << endl; }
private:
	int _a;
};
class B :public A {
    
    
public:
	virtual void func1() {
    
     cout << "  B::func1" << endl; }
	virtual void func2() {
    
     cout << "  B::func2" << endl; }
private:
	int _b;
};
class C :public A, public B
{
    
    
public:
	virtual void func1() {
    
     cout << "  B::func1" << endl; }
	virtual void func3() {
    
     cout << "  B::func3" << endl; }
private:
	int _c;
};
int main()
{
    
    
	C c;
	A* a = &c;
	B* b = &c;
	a->func1();
	b->func1();
	return 0;
}

insert image description here

First look at the above code, a and b call func1, calling the same function, the final result is the same, it can be considered that there is an address in it that has been encapsulated.
Let's take a brief look at their respective disassembly: as shown in the figure:
insert image description here

The first one is to call an address. This address is taken from the virtual table. This address is the jmp instruction. The jmp instruction then jmps an address to come to the real address of the function. is a normal call.
The second is, jmp several times in a row, which is equivalent to going around several times, because there is a key instruction sub, ecx, 8 in the middle: ecx stores the this pointer, a and b call the subclass function, and a calls Subclass function, the pointer is no problem, a points to the beginning of the object, this pointer points to the subclass object, and it just points to the beginning of the subclass object without processing; but b calls the this pointer and does not point to the beginning of the object, so this instruction is in Correct this pointer, minus 8 is minus the size of A. Whoever inherits who corrects.
In the end they came to the same address.

4. Abstract class

Write =0 behind the virtual function, then this function is a pure virtual function, and no implementation is written. A class containing pure virtual functions is called an abstract class or an interface class. It does not have a corresponding entity. If you do not want it to instantiate an object, a type that does not have a corresponding entity in reality can define a class as an abstract class.
The characteristic of abstract classes is that objects cannot be instantiated, and derived classes cannot instantiate objects after inheritance. Only by rewriting pure virtual functions can derived classes instantiate objects. One of the meanings of pure virtual functions is to force derived classes to rewrite, and pure virtual functions also reflect interface inheritance.

class P
{
    
    
public:
virtual void sleep() = 0;
};
class S :public P
{
    
    
public:
virtual void sleep()
{
    
    
cout << "Benz-舒适" << endl;
}
};
class T:public P
{
    
    
public:
virtual void sleep()
{
    
    
cout << "走路" << endl;
}
};
void Test()
{
    
    
P* s1 = new s;
s1->sleep();
P* t1 = new T;
t1->sleep();
}

5. Summary of polymorphic problems

Can an inline function be a virtual function?
Theoretically speaking, the inline function will be expanded at the place where it is called, and it has no address. If it is a virtual function, it must have an address in the virtual table, and the inline function cannot become a virtual function without an address. But in actual operation, it can be compiled because the compiler ignores the inline attribute. Adding lnline does not necessarily mean inlining. Inlining is just a suggestion for the compiler, so this function is no longer lnline, so you can put into the vtable. If it does not conform to polymorphism, it may follow the inline rules. If it is polymorphic, because the virtual function needs to put the address in the virtual table, there must be an address, so it cannot be inline. The actual running process can only be one attribute.
Can a static member be a virtual function?
No, it is inappropriate to put static member functions in the virtual table, because the virtual table is found through the pointed object. Static member functions do not have this pointer and can only be accessed by the object type, but this cannot access virtual functions. table, polymorphism cannot be achieved.
Can a constructor be virtual?
No, because the virtual function table pointer in the object is initialized at the constructor initialization list stage.
If the constructor implements polymorphism, the call needs to find the virtual function. How to find it, there must be a virtual function table pointer, and the virtual function table pointer is only available during the initialization phase of the calling constructor, so you have to wait for the initialization of the constructor to complete. After that, it can be called after the object is instantiated, but the constructor call is called before the object is instantiated, so it is contradictory, it cannot be a virtual function and is meaningless.
Can copy construction and assignment be virtual functions?
Copy construction is similar to construction. It is best not to define assignment as a virtual function, and the compiler can support it. The virtual function is to complete the rewriting, which means pointing to the parent class to adjust the parent class, and pointing to the subclass to adjust the subclass. After polymorphism, it is either the parent or the child. If the base class assignment function is defined as a virtual function, it will not affect the use of subclass assignment operators. Their return values ​​and formal parameters are different. If they are just the same as the base class and the assignment function is defined as a virtual function, A derived class cannot be either parent or child, and its child has to adjust the parent in turn, which is meaningless.
Can a friend function be virtual?
No, because it is not a member function and cannot be inherited
Can a destructor be a virtual function?
Yes, and it is better to define the destructor of the base class as a virtual function, and the appropriate destructor will be called according to the dynamic object. You can delete the parent class pointer pointing to the subclass to deconstruct the subclass. If the base class pointer points to a subclass object, slicing will occur. This process is to cut off those members that the base class does not have. If the destructor is not defined as a virtual function, when the base class is deleted, the part of the base class will be destructed. , and the subclass is not destructed. If the subclass destructs and releases resources, it will cause a memory leak.
Is object access to ordinary functions faster or virtual functions faster?
If it is an object, no matter whether it is a virtual function or not, it is just as fast, because it is a normal call; if it is a parent class pointer or reference, only a virtual function is included, and the normal function is faster, because it is a polymorphic call regardless of whether the structure constitutes rewriting or not. It is not an ordinary call, and the compiler does not want to recognize it as an ordinary call, because the cost of it is too high and needs to be judged. Therefore, when polymorphic calls need to find virtual functions in the virtual function table, it is slow.
Where does the virtual function without rewriting go?
As long as it is a virtual function, it enters the virtual table. In single inheritance, the virtual function that has not been rewritten in the subclass enters its own virtual table. In multiple inheritance, the virtual function that does not have rewriting enters the first parent class. Virtual table.
Virtual function table and virtual base table
The virtual function stores the address of the virtual function, which realizes polymorphism, and refers to who calls whom; the virtual base table stores offsets, which solve data redundancy and ambiguity.
Overloading | Overriding (overriding) | Hidden comparison
Overloading: Two functions are in the same scope, with the same function name and different parameters.
Rewriting/covering: Two functions are in the scope of the parent and child classes respectively, the function name, parameters, and return value must be the same (except for covariance), and the two functions must be virtual functions.
Redefinition/hiding: The two functions are in the scope of the parent and child classes respectively, and the function names are the same. The functions with the same name in the parent and child classes do not constitute rewriting or redefinition.

Guess you like

Origin blog.csdn.net/m0_59292239/article/details/130131722