菱形继承及内部实现
2016-03-12 22:53
393 查看
学习了C++的继承后,觉得菱形继承是比较难懂的一部分,通过了解菱形继承,我们可以了解编译器是如何工作的,下面介绍一下菱形继承在内存中的实现方式。
首先简单了解一下虚继承,下面父类和子类的大小分别是多少?
为什么?是怎么实现的呢?
由于类Base中存在内存对齐,还包含了虚函数,则含有指向虚函数表的指针,故Base类的大小为4+4+4=12。类Derive里包含:继承的虚函数类,该类的int _b,还有一个指向虚基类的指针。考虑内存对齐,总大小为12+4+4=20,问题是多余的4个字节呢?下面通过介绍菱形继承进行分析。
菱形继承
从监视可以看出:Base1、Base2中都有func1();Base1的_vfptr与Base2的_vfptr地址不同,指向的内容也不同。由于Base1的虚表与Base2的虚表都含有Base的fun1(),这种继承存在二义性与冗余性。菱形虚继承解决了这个问题,在定义 Base1,Base2时,需要在Base1和Base2类中的public Base前加 virtual。菱形虚继承运行结果:
Base1的_vfptr与Base2的_vfptr地址相同,菱形虚拟继承比菱形继承多了一个虚表,专门存放Base,可见将超类存放一份,通过指针使用该类,这样子类中父类与超类公共部分都是同一块存储空间,就解决了二义性与数据冗余问题
指针_vfptr:0x0031F6C8和0x0031F6D40x0031F6C8 + 0xFFFFFFFC(-4) = 0x0031F6C4, 0x0031F6C8 + 0x00000018 = 0x0031F6EC; 0x0031F6D4 + 0xFFFFFFFC(-4) = 0x0031F6D0, 0x0031F6D4 + 0x0000000c = 0x0031F6EC.结论如下: 在虚继承时,类中会自动加一个指针(_vfptr),该变量指向一个全类共享的偏移量表。
如上图所示偏移量的说明:如果该类有虚函数,那么第一项记录着当前子对象相对与虚基类表指针的偏移,是FF FF FF FC(也就是-4),如果没有则是零;第二项是被继承的基类(Base类)子对象(d._a)相对于_vfptr指针的偏移量。
首先简单了解一下虚继承,下面父类和子类的大小分别是多少?
class Base { public: virtual void fun1() { cout << "Base::fun1()" << endl; } public: int _a; char a; }; class Derive:virtual public Base { public: virtual void fun1() { cout << "Derive::fun1()" << endl; } virtual void fun2() { cout << "Derive::fun2()" << endl; } public: int _b; };上述由于内存对齐,Base类的大小为12,Derive类的大小为24。
为什么?是怎么实现的呢?
由于类Base中存在内存对齐,还包含了虚函数,则含有指向虚函数表的指针,故Base类的大小为4+4+4=12。类Derive里包含:继承的虚函数类,该类的int _b,还有一个指向虚基类的指针。考虑内存对齐,总大小为12+4+4=20,问题是多余的4个字节呢?下面通过介绍菱形继承进行分析。
菱形继承
#include<iostream> using namespace std; typedef void (*FUNC )();//定义函数类型指针 class Base //超类 { public: virtual void fun1() { cout << "Base::fun1()" << endl; } public: int _a; }; class Base1 :public Base //父类 { public: virtual void fun1() { cout << "Base1::fun1()" << endl; } virtual void fun2() { cout << "Base1::fun2()" << endl; } public: int _b; }; class Base2 :public Base //父类 { public: virtual void fun1() { cout << "Base2::fun1()" << endl; } virtual void fun3() { cout << "Base2::fun3()" << endl; } public: int _c; }; class Derive :public Base1 ,public Base2 //子类 { public: virtual void fun1() { cout << "Derive::fun1()" << endl; } virtual void fun2() { cout << "Derive::fun2()" << endl; } virtual void fun3() { cout << "Derive::fun3()" << endl; } virtual void fun4() { cout << "Derive::fun4()" << endl; } public: int _d; }; void PrintTable(int * vTable )//打印出虚函数表 {//虚函数表中结束标志是NULL for ( int i = 0; vTable[i] != 0; i++) { printf( "第%d个虚函数->%p\n" , i, vTable [i]); FUNC f = ( FUNC) vTable[i]; f(); } cout<<endl; } void Test() { Base a; Base1 b; Base2 c; Derive d; cout << "Base->" << sizeof (a) << endl; cout << "Base1->" << sizeof (b) << endl; cout << "Base2->" << sizeof (c) << endl; cout << "Derive->" << sizeof (d) << endl; //d._a = 1;此写法存在二义性,无法访问 d. Base1::_a = 1; //不能从根本上解决二义性 d._b = 2; d._c = 3; d._d = 4; int *vTable = (int*)&d; int *vTable1 = (int*)*(int*)&d; int *vTable2 = (int*)(*((int*)&d + sizeof(Base1) / 4)); cout << "虚函数表地址:" << vTable << endl; cout << "虚函数表——第一个函数地址:" << vTable1 << endl; PrintTable(vTable1); cout << "虚函数表——第二个函数地址:" << vTable2 << endl; PrintTable(vTable2); }菱形继承运行结果如下:
从监视可以看出:Base1、Base2中都有func1();Base1的_vfptr与Base2的_vfptr地址不同,指向的内容也不同。由于Base1的虚表与Base2的虚表都含有Base的fun1(),这种继承存在二义性与冗余性。菱形虚继承解决了这个问题,在定义 Base1,Base2时,需要在Base1和Base2类中的public Base前加 virtual。菱形虚继承运行结果:
Base1的_vfptr与Base2的_vfptr地址相同,菱形虚拟继承比菱形继承多了一个虚表,专门存放Base,可见将超类存放一份,通过指针使用该类,这样子类中父类与超类公共部分都是同一块存储空间,就解决了二义性与数据冗余问题
指针_vfptr:0x0031F6C8和0x0031F6D40x0031F6C8 + 0xFFFFFFFC(-4) = 0x0031F6C4, 0x0031F6C8 + 0x00000018 = 0x0031F6EC; 0x0031F6D4 + 0xFFFFFFFC(-4) = 0x0031F6D0, 0x0031F6D4 + 0x0000000c = 0x0031F6EC.结论如下: 在虚继承时,类中会自动加一个指针(_vfptr),该变量指向一个全类共享的偏移量表。
如上图所示偏移量的说明:如果该类有虚函数,那么第一项记录着当前子对象相对与虚基类表指针的偏移,是FF FF FF FC(也就是-4),如果没有则是零;第二项是被继承的基类(Base类)子对象(d._a)相对于_vfptr指针的偏移量。
相关文章推荐
- C++多重继承与虚继承分析
- C++虚继承的古怪行为
- 关于字节对齐和继承的关系
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- 关于C++中的虚拟继承的一些总结
- 虚函数与虚继承寻踪
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- RTTI、虚函数和虚基类的开销分析及使用指导
- C++学习之多重继承与虚继承
- 继承 虚继承 结构体 为内存布局抛砖引玉
- C++中必须要知道的知识
- C++ - 虚继承(virtual inheritance)
- C++ - 虚继承(virtual inheritance)的构造(construct)和析构(destruct)顺序
- C++ - 虚继承(virtual inheritance) 详解
- C++ - 类的虚函数\虚继承所占的空间
- 【C++基础之二十一】菱形继承和虚继承
- C++基础之--------虚拟继承的类对象的空间大小
- C++中虚函数的内部实现
- C++多重继承中的虚继承和虚函数举例
- C++多重继承中的虚继承举例