Detailed explanation of the relationship between virtual function table and virtual function pointer

Posted by varecha on Thu, 16 Dec 2021 21:43:27 +0100

Is the virtual function table a class?
1. The virtual function table belongs to a class, and all objects of the class share the virtual function table of this class.
2. The virtual function tables of different objects are the same (the first function address of the virtual function table is the same);
3. A pointer vptr pointing to the virtual function table is stored inside each object. The storage address of vptr of each object is different, but they all point to the same virtual function table.
If a class contains virtual functions, the attribution and storage location of its virtual function table and virtual function pointer are as follows

When an object calls a virtual function, the actual called function is determined by the following steps: find the vtbl pointed to by the object's vptr, and then find the appropriate function pointer in the vtbl.

Examples of virtual function tables and virtual function pointers:

class Base1{
public:
	Base1(int i){m_i1=i;}
	~Base1(){ cout << "Base1 deconstruct" << endl;}
	virtual void f1()
	{
		cout << "Base1::f1()" << endl;
	}
	virtual void g1()
	{
		cout << "Base1::g1()" << endl;
	}
private:
	int m_i1;
};

Print this virtual function table: (traverse with function pointer)

   Base1 base1(1);

cout<<"Base1 size"<<sizeof(base1)<<endl;
long *p0 = (long *)(*(long*)&base1);
PrintVTable(p0); 
cout<<endl;
cout<<endl;
cout<<endl;
Base1 base11(2);
cout<<"Base11 size"<<sizeof(base11)<<endl;
long *p1 = (long *)(*(long*)&base11);
PrintVTable(p1); 

result:

Conclusion: all objects of a class share a virtual function table (the virtual function address is the same).

Single inheritance view virtual function table and virtual function pointer

class Base3:public Base1{//Single inheritance
public:
	// Test1(int d1, int d2) :Test(d2) { data1 = d1; data2 = d2; }
	Base3(int i1,int i2):Base1(i2){m_i3=i1;}
	~Base3(){ cout << "Base3 deconstruct" << endl;}
	virtual void f1()//cover
	{
		cout << "Base3::f1()" << endl;
	}
	virtual void f3()//Additional virtual function
	{
		cout << "Base3::f3()" << endl;
	}
	virtual void g3()//Additional virtual function
	{
		cout << "Base3::g3()" << endl;
	}
private:
	int m_i3;
};

Print virtual function table:

Base3 base3(1,2);
cout<<"Base3 size"<<sizeof(base3)<<endl;
long *p2 = (long *)(*(long*)&base3);
PrintVTable(p2); 

result:

Conclusion: the derived class inherits the virtual function table of the base class. Base3 inherits base1 and overwrites f1. In the virtual function table base3, the address of f1 is overwritten with base3. At the same time, the addresses of other self-defined virtual functions f3 and g3 are added to the back of the virtual function table.

Multiple inheritance:

class Base2{
public:
	Base2(int i){m_i2=i;}
	~Base2(){ cout << "Base2 deconstruct" << endl;}
	virtual void f2()
	{
		cout << "Base2::f2()" << endl;
	}
	virtual void g2()
	{
		cout << "Base2::g2()" << endl;
	}
private:
	int m_i2;
};
class Base4:public Base1,public Base2{//Multiple inheritance
public:
	Base4(int i1,int i2,int i3):Base1(i2),Base2(i3){m_i4=i1;}
	~Base4(){ cout << "Base4 deconstruct" << endl;}
	virtual void f1()//Override base class 1
	{
		cout << "Base4::f1()" << endl;
	}
	virtual void g2()//Cover base class 2
	{
		cout << "Base4::g2()" << endl;
	}
	virtual void f4()//Additional virtual function
	{
		cout << "Base4::f4()" << endl;
	}
	virtual void g4()//Additional virtual function
	{
		cout << "Base4::g4()" << endl;
	}
private:
	int m_i4;
};

Print this virtual function table:

      Base4 base4(1,2,3);
cout<<"Base3 size"<<sizeof(base4)<<endl;
long *p3 = (long *)(*(long*)&base4);
PrintVTable(p3); 

result:

Conclusion:
In the case of multiple inheritance, there are multiple virtual function tables in the derived class, and the arrangement of virtual functions is consistent with the order of inheritance. The derived class rewriting function will overwrite the contents of all virtual function tables with the same name. The new virtual function defined by the derived class will be expanded after the virtual function table of the first class.

Additional source code:

class Base1{
public:
	Base1(int i){m_i1=i;}
	~Base1(){ cout << "Base1 deconstruct" << endl;}
	virtual void f1()
	{
		cout << "Base1::f1()" << endl;
	}
	virtual void g1()
	{
		cout << "Base1::g1()" << endl;
	}
private:
	int m_i1;
};
class Base2{
public:
	Base2(int i){m_i2=i;}
	~Base2(){ cout << "Base2 deconstruct" << endl;}
	virtual void f2()
	{
		cout << "Base2::f2()" << endl;
	}
	virtual void g2()
	{
		cout << "Base2::g2()" << endl;
	}
private:
	int m_i2;
};
class Base3:public Base1{//Single inheritance
public:
	// Test1(int d1, int d2) :Test(d2) { data1 = d1; data2 = d2; }
	Base3(int i1,int i2):Base1(i2){m_i3=i1;}
	~Base3(){ cout << "Base3 deconstruct" << endl;}
	virtual void f1()//cover
	{
		cout << "Base3::f1()" << endl;
	}
	virtual void f3()//Additional virtual function
	{
		cout << "Base3::f3()" << endl;
	}
	virtual void g3()//Additional virtual function
	{
		cout << "Base3::g3()" << endl;
	}
private:
	int m_i3;
};
class Base4:public Base1,public Base2{//Multiple inheritance
public:
	Base4(int i1,int i2,int i3):Base1(i2),Base2(i3){m_i4=i1;}
	~Base4(){ cout << "Base4 deconstruct" << endl;}
	virtual void f1()//Override base class 1
	{
		cout << "Base4::f1()" << endl;
	}
	virtual void g2()//Cover base class 2
	{
		cout << "Base4::g2()" << endl;
	}
	virtual void f4()//Additional virtual function
	{
		cout << "Base4::f4()" << endl;
	}
	virtual void g4()//Additional virtual function
	{
		cout << "Base4::g4()" << endl;
	}
private:
	int m_i4;
};

typedef void(*FUNC)();        //Redefine function pointer, pointer to function
void PrintVTable(long* vTable)  //Print virtual function table
{
	if (vTable == NULL)
	{
		return;
	}
	cout << "vtbl:" << vTable << endl;
	int  i = 0;
	for (; vTable[i] != 0; ++i)
	{
		printf("function : %d :0X%x->", i, vTable[i]);
		FUNC f = (FUNC)vTable[i];
		f();         //Accessing virtual functions
	}
	cout << endl;
}

int main()
{
	Base1 base1(1);
	cout<<"Base1 size"<<sizeof(base1)<<endl;
	long *p0 = (long *)(*(long*)&base1);
	PrintVTable(p0); 
	cout<<endl;
	cout<<endl;
	cout<<endl;
	Base1 base11(2);
	cout<<"Base11 size"<<sizeof(base11)<<endl;
	long *p1 = (long *)(*(long*)&base11);
	PrintVTable(p1); 
	
	
	cout<<endl;
	cout<<endl;
	cout<<endl;
	Base3 base3(1,2);
	cout<<"Base3 size"<<sizeof(base3)<<endl;
	long *p2 = (long *)(*(long*)&base3);
	PrintVTable(p2); 
	
	
	cout<<endl;
	cout<<endl;
	cout<<endl;
	Base4 base4(1,2,3);
	cout<<"Base3 size"<<sizeof(base4)<<endl;
	long *p3 = (long *)(*(long*)&base4);
	PrintVTable(p3); 
	
	system("pause");
	return 0;
	
}

Topics: C++ OOP Polymorphism