c++单继承、多继承、菱形继承内存布局(虚函数表结构)
c++
1、单继承的内存布局
单继承:只有一个基类和一个派生类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class Base { public: virtual void fun1() { cout << "Base::func1()" << endl; } virtual void fun2() { cout << "Base::func2()" << endl; } private: int b; }; class Derive :public Base { public: virtual void fun1() { cout << "Derive::func1()" << endl; }
virtual void fun3() { cout << "Derive::func3()" << endl; } void fun4() { cout << "Derive::func4()" << endl; } private: int d; };
|
在VS中调试的时候可以看到一些虚函数表,但有可能不全,所以我们在这里先写一个函数用于打印虚函数表!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| typedef void (*FUNC)();
void PrintVTable(int* vTable,int k) { if (vTable == NULL) { return; } cout << "虚函数表地址:" << vTable << endl; int i = 0; for (; vTable[i] != 0&&i<k; ++i) { printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]); FUNC f = (FUNC)vTable[i]; f(); } cout << endl; }
|
测试函数:
1 2 3 4 5 6 7 8 9
| void Test1() { Base b; Derive d; int* tmp = (int*)(*(int*)&b); PrintVTable(tmp); int* tmp1 = (int*)(*(int*)&d); PrintVTable(tmp1); }
|
输出:
1 2 3 4 5 6 7 8
| 虚函数表地址:0x4c14f0 第0个虚函数地址 :0X438690,->Base::func1() 第1个虚函数地址 :0X4386c4,->Base::func2()
虚函数表地址:0x4c1500 第0个虚函数地址 :0X438728,->Derive::func1() 第1个虚函数地址 :0X4386c4,->Base::func2() 第2个虚函数地址 :0X43875c,->Derive::func3()
|
解析:int* tmp = (int)( (int*)&b);
如下图:
2、多继承的内存布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| class Base1 //基类 { public: virtual void fun1() { cout << "Base1::fun1" << endl; } virtual void fun2() { cout << "Base1::fun2" << endl; } private: int b1; }; class Base2 //基类 { public: virtual void fun1() { cout << "Base2::fun1" << endl; } virtual void fun2() { cout << "Base2::fun2" << endl; } private: int b2; }; class Derive : public Base1, public Base2 { public: virtual void fun1() { cout << "Derive::fun1" << endl; } virtual void fun3() { cout << "Derive::fun3" << endl; } private: int d1; };
|
测试函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| void Test1() { Base1 b1; int* tmp1 = (int*)(*(int*)&b1); PrintVTable(tmp1,2);
Base2 b2; int* tmp2 = (int*)(*(int*)&b2); PrintVTable(tmp2,2);
Derive d1; int* VTable = (int*)(*(int*)&d1); PrintVTable(VTable, 3); VTable = (int*)(*((int*)&d1 + sizeof (Base1)/4)); PrintVTable(VTable, 2); }
|
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 虚函数表地址:0x4c1510 第0个虚函数地址 :0X4386e0,->Base1::fun1 第1个虚函数地址 :0X438714,->Base1::fun2
虚函数表地址:0x4c1520 第0个虚函数地址 :0X438778,->Base2::fun1 第1个虚函数地址 :0X4387ac,->Base2::fun2
虚函数表地址:0x4c1530 第0个虚函数地址 :0X438810,->Derive::fun1 第1个虚函数地址 :0X438714,->Base1::fun2 第2个虚函数地址 :0X438844,->Derive::fun3
虚函数表地址:0x4c1544 第0个虚函数地址 :0X4b5860,->Derive::fun1 第1个虚函数地址 :0X4387ac,->Base2::fun2
|
解析:VTable = (int*)(((int)&d1 + sizeof (Base1)/4));
3、菱形继承(非虚继承)的内存布局
菱形继承会出现子对象重叠的问题。
子对象重叠:两个子类base1和base2继承同一父类base,而又有子类继承这两个子类。会产生二义性问题,即对于base的调用需要指定作用域。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| class Base //Derive的间接基类 { public: virtual void func1() { cout << "Base::func1()" << endl; } virtual void func2() { cout << "Base::func2()" << endl; } private: int b; }; class Base1 :public Base { public: virtual void func1() { cout << "Base1::func1()" << endl; } virtual void func3() { cout << "Base1::func3()" << endl; } private: int b1; }; class Base2 :public Base { public: virtual void func1() { cout << "Base2::func2()" << endl; } virtual void func4() { cout << "Base2::func4()" << endl; } private: int b2; }; class Derive :public Base1, public Base2 { public: virtual void func1() { cout << "Derive::func1()" << endl; } virtual void func5() { cout << "Derive::func5()" << endl; } private: int d; };
|
测试函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| void Test1() { Base b; Base1 b1; Base2 b2; Derive d; printf("sizeof(Base) = %d\n",sizeof(b)); printf("sizeof(Base1) = %d\n",sizeof(b1)); printf("sizeof(Base2) = %d\n",sizeof(b2)); printf("sizeof(Derive) = %d\n",sizeof(d));
int* tmp = (int*)(*(int*)&b); PrintVTable(tmp,2);
int* tmp1 = (int*)(*(int*)&b1); PrintVTable(tmp1,3);
int* tmp2 = (int*)(*(int*)&b2); PrintVTable(tmp2,3);
int* VTable = (int*)(*(int*)&d); PrintVTable(VTable, 4); VTable = (int*)(*((int*)&d + sizeof (Base1)/4)); PrintVTable(VTable, 3); }
|
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| sizeof(Base) = 8 sizeof(Base1) = 12 sizeof(Base2) = 12 sizeof(Derive) = 28 Base虚函数表地址:0x4c25d0 第0个虚函数地址 :0X4387b0,->Base::func1() 第1个虚函数地址 :0X4387e4,->Base::func2()
Base1虚函数表地址:0x4c25e0 第0个虚函数地址 :0X438848,->Base1::func1() 第1个虚函数地址 :0X4387e4,->Base::func2() 第2个虚函数地址 :0X43887c,->Base1::func3()
Base2虚函数表地址:0x4c25f4 第0个虚函数地址 :0X4388f0,->Base2::func2() 第1个虚函数地址 :0X4387e4,->Base::func2() 第2个虚函数地址 :0X438924,->Base2::func4()
Derive虚函数表地址:0x4c2608 第0个虚函数地址 :0X438998,->Derive::func1() 第1个虚函数地址 :0X4387e4,->Base::func2() 第2个虚函数地址 :0X43887c,->Base1::func3() 第3个虚函数地址 :0X4389cc,->Derive::func5()
虚函数表地址:0x4c2620 第0个虚函数地址 :0X4b59f0,->Derive::func1() 第1个虚函数地址 :0X4387e4,->Base::func2() 第2个虚函数地址 :0X438924,->Base2::func4()
|
注意:两个func2()是不同的函数,即使都是继承于Base。
内存布局如图:
4、菱形继承(虚继承)的内存布局
关于虚继承的两种内存布局方案
我们主要解析第一种方案:
vs2003下虚继承的VBPTR及VBTBL:
在类中增加一个指针(VBPTR)指向一个VBTBL,这个VBTBL的第一项记载的是从VBPTR 与本类的偏移地址,如果本类有虚函数,那么第一项是FF FF FF FC(也就是-4),如果没有则是零,第二项起是VBPTR与本类的虚基类的偏移值。
代码:菱形继承(虚继承)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| class Base { public: virtual void fun1() { cout << "Base::fun1()" << endl; } virtual void fun2() { cout << "Base::fun2()" << endl; } private: int b; }; class Base1 :virtual public Base 虚继承 { public: virtual void fun1() { cout << "Base1::fun1()" << endl; } virtual void fun3() { cout << "Base1::fun3()" << endl; } private: int b1; }; class Base2 :virtual public Base { public: virtual void fun1() { cout << "Base2::fun1()" << endl; } virtual void fun4() { cout << "Base2::fun4()" << endl; } private: int b2; }; class Derive :public Base1, public Base2 { public: virtual void fun1() { cout << "Derive::fun1()" << endl; } virtual void fun5() { cout << "Derive::fun5()" << endl; } private: int d; };
|
1. 详细地分析一下vs2003下虚继承的VBPTR及VBTBL:
以Base1 b1;为例子,详细分析内存布局如下(Base2和Base1的内存布局相似):
2、我们再来探索一下 Derive d 的内存布局
我们怎么验证菱形继承(虚继承)的内存布局就是这样的呢?我们可以通过打印虚表!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| typedef void(*FUNC)(); void PrintVPTR(int* VPTR) { cout << "虚函数表地址:" << VPTR << endl; for (int i = 0; VPTR[i] != 0; ++i) { printf("第%d个虚函数地址:0X%x->", i, VPTR[i]); FUNC f = (FUNC)VPTR[i]; f(); } cout << endl; } void PrintVBPTR(int* VBPTR) { cout << "虚函数表地址:" << VBPTR << endl; int i = 0; printf("与本类的偏移地址:0X%x\n", VBPTR[i]); for (i = 1; VBPTR[i] != 0; i++) { cout << VBPTR[i] << " " << endl; } cout << endl; }
|
(本地测试失败)
原文地址:https://blog.csdn.net/u010235142/article/details/78307022