C + + virtual table and virtual destructor

In C + +, the realization of polymorphism is related to the concept of binding. The process of compiling and linking a source program into an executable file is the process of binding (or assembling) the executable code together. Among them, the connection completed before operation becomes static connection (early connection); The binding completed only when the program is running is called dynamic binding (later binding).

The polymorphism supported by static binding is called compile time polymorphism (static polymorphism). In C + +, compile time polymorphism is realized by function overloading and template. Using the function overloading mechanism, when calling a function with the same name, the compiling system will determine which function to call according to the specific conditions of the arguments.

The polymorphism supported by dynamic binding is called runtime polymorphism (dynamic polymorphism). In C + +, runtime polymorphism is realized by virtual functions.

01. Implementation of dynamic polymorphism

#include<iostream>
using namespace std;

class father
{
public:
	virtual void show();
};
class son:public father
{
public:
	virtual void show();
};
void father::show()
{
	cout << "father" << endl;
}
void son::show()
{
	cout << "son" << endl;
}
int main()
{
	father f1;
	father* pt = &f1;
	pt->show();
	son s1;
	pt = &s1;
	pt->show();
}


It can be seen that although pt is a pointer to the father type, the class of the virtual function called is determined according to the class pointed to by the current pt

2. Why is the size of C + + hollow class 1 byte?

1. For the problem that the size of structure and empty class is 1 byte, first of all, it is a C + + problem. In C language, the size of empty structure is 0 (of course, this is compiler related). The empty class and empty structure here means that there are no members in the class or structure.

2. In C + +, the size of empty class and empty structure is 1 (compiler related). Why? Why not 0?

3. This is because the C + + standard stipulates that "no object shall have the same address in memory as any other variable", that is, any different object cannot have the same memory address. If the empty class size is 0, if we declare an array of objects of this class, then each object in the array has the same address, which is obviously against the standard.

3. Check the size of the empty class, then add the virtual function and check it again
Define an empty class (without any explicit members)

class kong
{

};
int main()
{
	kong k1;
	cout << sizeof(k1)<<endl;
	cout << sizeof(kong)<<endl;
}


You can see that the space occupied by the class and its instantiated object is the same, which is one byte
Add a non virtual function to it and use the same main function:

class kong
{
	void kong1() {};
};


The result is the same
Add virtual function:

class kong
{
	void kong1() {};
	virtual void kongv() {};
};


Add int type (4 bytes under 64 bit system) data member:

class kong
{
	void kong1() {};
	virtual void kongv() {};
	int a;
};


Therefore, we can guess that the memory model of the class is virtual function table pointer and data member:

vtptr occupies four bytes, and i is its data member

4. Verify the memory model
First, add a data member a and a virtual function show2 () for the father class

class father
{
public:
	virtual void show1();
	virtual void show2();
	int a=1;
};
void father::show2()
{
	cout << "father2" << endl;
}
int main()
{
	father f1;
	father* pt = &f1;
	pt->show1();
	cout << *(unsigned int*)(&f1) << endl;
	father f2;
	cout << *(unsigned int*)(&f2) << endl;
	cout << *((unsigned int*)(&f2)+1) << endl;	
}

(& F1) is the address of the memory model. Re dereference means taking the first member, and + 1 re dereference means taking the second member
The first member in the f1 memory model, the virtual function table pointer, displays the first address of the virtual table

You can see that the virtual table addresses of different instantiated objects of the same class are the same

int main()
{
	father f1;
	father* pt = &f1;
	cout << (unsigned int*)(&f1) << endl;
	father f2;
	cout << (unsigned int*)(&f2) << endl;
}


It can be seen that even for objects of the same class, the addresses of their models are different, and the pointers to the virtual table are not the same
Therefore, we conclude that the virtual function table pointer corresponds to the object one by one, and the virtual table corresponds to the class one by one

The schematic diagram is as follows:

5. View virtual function table memory model
Since the address of void type function cannot be converted and printed, replace show1 and show2 above with int type
And add the virtual function show2 () in the parent class

int main()
{
	father f1;
	father* pt = &f1;
	son s1;
	cout << "f1 Memory model address"<<(unsigned int*)(&f1) << endl;
	cout << "s1 Memory model address" << (unsigned int*)(&s1) << endl;
	cout <<"father Class virtual table address"<< *(unsigned int*)(&f1) << endl;
	cout << "son Class virtual table address" << *(unsigned int*)(&s1) << endl;
	cout << "adopt father Class virtual function table view father In class show1()Function address" << *(unsigned int*)*(unsigned int*)(&f1)<< endl;
	cout << "adopt father Class virtual function table view father In class show2()Function address"<<*(unsigned int*)(*(unsigned int*)(&f1)+1) << endl;
	cout << "adopt son Class virtual function table view father In class show1()Function address" << *(unsigned int*)*(unsigned int*)(&s1) << endl;
	cout << "adopt son Class virtual function table view father In class show2()Function address" << *(unsigned int*)(*(unsigned int*)(&s1)+1) << endl;
}

The address of the same virtual function accessed by the virtual function table of the parent and derived class objects that can be viewed by accessing the second element of the virtual function table is the same
The schematic diagram is as follows:

6. Use of virtual functions
Go back to the use of virtual functions

int main()
{
	father f1;
	father* pt = &f1;
	pt->show();
	son s1;
	pt = &s1;
	pt->show();
}

When pt points to f1, calling the virtual function show1() will first find the virtual function table through the virtual function table pointer of f1, and then find the show1 of the Father class through the address of show1() of the virtual function table

When pt points to s1, the virtual function table at this time is the virtual function table of son class, and show1() of son will be called;

7. Virtual deconstruction

Keywords: C++ Back-end

Added by steelmanronald06 on Thu, 27 Jan 2022 02:25:56 +0200