C++内存布局(下)
2016-03-07 19:08
351 查看
上一篇已经分析了基本结构体、C++简单对象和加上一般的继承之后的各种情况,这篇文章将主要在有虚继承时,C++对象的内存又是怎样分配的。
虚继承的一般类图如下所示:
sizeof(Derived) = 40 字节。经过分析,可以得到Derived类在内存中的存储结构示意图为:
对于虚拟继承,子类中会增加一个vbptr指针,它指向的值要么是0,要么是-4,表示公共基类相对于子类的偏移,也即Base类相对于Derived类的偏移。当Base类中有虚函数时,为-4;否则为0。
在VS编译器中,子类和公共基类之间会通过一个NULL指针分隔开。在其它编译器中可能没有这个字段
对于每个类的虚函数表可以依次分析:
首先是Base类,Base类的ff()函数首先被Base1覆盖,然后又被Derived类覆盖,Base类的gg()函数并未被覆盖,因此Base类的虚函数表中指向的虚函数分别为:Derived::ff()、Base::gg()。
然后是Base1类,Base1类原本定义了两个虚函数,但是ff()函数是覆盖了父类Base中的函数,因此不在Base1的虚函数表中,只有bf1()。同时子类Derived中新定义了一个hh()虚函数,要插入到内存中的第一个虚函数表,因此会插入到Base1类的虚函数表中,因而Base1类中会有Base1::bf1()、Derived::hh()两个函数
最后是Base2类,只有Base2::bf2()一个虚函数。
简单的验证代码如下所示:
得到输出结果为:
虚继承
虚继承解决了从不同途径继承的类具有共同基类的问题,使得共同基类只有一份拷贝。解决了二义性的问题,也节省了内存。虚继承的一般类图如下所示:
sizeof(Derived) = 40 字节。经过分析,可以得到Derived类在内存中的存储结构示意图为:
总结:
各部分在内存中的存放顺序为先父类、后子类、最后公共基类,即先存放Base1、Base2,然后是Derived,最后才是Base类对于虚拟继承,子类中会增加一个vbptr指针,它指向的值要么是0,要么是-4,表示公共基类相对于子类的偏移,也即Base类相对于Derived类的偏移。当Base类中有虚函数时,为-4;否则为0。
在VS编译器中,子类和公共基类之间会通过一个NULL指针分隔开。在其它编译器中可能没有这个字段
对于每个类的虚函数表可以依次分析:
首先是Base类,Base类的ff()函数首先被Base1覆盖,然后又被Derived类覆盖,Base类的gg()函数并未被覆盖,因此Base类的虚函数表中指向的虚函数分别为:Derived::ff()、Base::gg()。
然后是Base1类,Base1类原本定义了两个虚函数,但是ff()函数是覆盖了父类Base中的函数,因此不在Base1的虚函数表中,只有bf1()。同时子类Derived中新定义了一个hh()虚函数,要插入到内存中的第一个虚函数表,因此会插入到Base1类的虚函数表中,因而Base1类中会有Base1::bf1()、Derived::hh()两个函数
最后是Base2类,只有Base2::bf2()一个虚函数。
简单的验证代码如下所示:
typedef void (*Fun) (void ); class Base{ public: Base():ba(333){} int ba; virtual void ff(){ cout<<"Base::ff()"<<endl; } virtual void gg(){ cout<<"Base::gg()"<<endl; } }; class Base1:virtual public Base{ public: Base1():ba(11){} int ba; virtual void ff(){ cout<<"Base::ff()"<<endl; } virtual void Bf1(){ cout<<"Base::Bf1()"<<endl; } }; class Base2:virtual public Base{ public: Base2():ba(22){} int ba; virtual void Bf2(){ cout<<"Base2::Bf2()"<<endl; } }; class Derived:public Base1,public Base2{ public: Derived():da(55){Base1();Base2();} int da; void ff(){ cout<<"Derived::ff()"<<endl; } virtual void hh(){ cout<<"Derived::hh()"<<endl; } }; int main(void){ Derived *pt=new Derived(); printf("sizeof(Derived) = %d\n",sizeof(Derived)); int len=sizeof(Derived)/4; int** pVtab = (int**)pt; Fun pFun; cout<<"pt[0] :"<<endl; for (int i=0; (Fun)pVtab[0][i]!=NULL; i++){ pFun = (Fun)pVtab[0][i]; pFun(); } cout<<"pt[1] :"<<endl; cout << " ["<<0<<"] "; cout<<(int)*pVtab[1]<<endl; cout<<"pt[2] :"<<(int)pVtab[2]<<endl; cout<<"pt[3] :"<<endl; for (int i=0; (Fun)pVtab[3][i]!=NULL; i++){ pFun = (Fun)pVtab[3][i]; pFun(); } cout<<"pt[4] :"<<endl; cout << " ["<<0<<"] "; cout<<(int)*pVtab[4]<<endl; cout<<"pt[5] :"<<(int)pVtab[5]<<endl; cout<<"pt[6] :"<<(int)pVtab[6]<<endl; cout<<"pt[7] :"<<(int)pVtab[7]<<endl; cout<<"pt[8] :"<<endl; pFun = (Fun)pVtab[8][0]; for(int i=0;(pFun = (Fun)pVtab[8][i])!=NULL;++i){ pFun(); } cout<<"pt[9] :"<<(int)pVtab[9]<<endl; system("pause"); return 0; }
得到输出结果为:
相关文章推荐
- c++编译器对多态的实现原理总结
- YTU 2425: C语言习题 输出月份
- c语言技巧
- YTU 2420: C语言习题 不等长字符串排序
- YTU 2419: C语言习题 等长字符串排序
- C++关键字之:explicit
- YTU 2424: C语言习题 字符串比较
- C++ Builder 中去除字符串中的指定字符
- YTU 2417: C语言习题 字符串长度
- C++字符串学习(二)
- c++,dll后缀的控件调用主程序本身的函数方法
- YTU 2429: C语言习题 学生成绩输入和输出
- C++风格的注释
- C语言-基础
- c++ 版本 opencv 读取视频
- 关于C++泛型及C++高效迭代开发的思考
- [原创]配置Dev c++热键实现快速注释
- 面试经验(妙计旅行:C++算法工程师)
- 最长递增子序列问题
- C++中给类初始化与赋值的区别