虚继承和虚函数继承

1、对虚继承层次的对象的内存布局,在不同编译器实现有所区别。

首先,说说GCC的编译器.

它实现比较简单,不管是否虚继承,GCC都是将虚表指针在整个继承关系中共享的,不共享的是指向虚基类的指针。

class A {

int a;

virtual ~A(){}

};

class B:virtual public A{

virtual void myfunB(){}

};

class C:virtual public A{

virtual void myfunC(){}

};

class D:public B,public C{

virtual void myfunD(){}

};

以上代码中sizeof(A)=8,sizeof(B)=12,sizeof(C)=12,sizeof(D)=16.

解释:Aint+虚表指针BC中由于是虚继承因此大小为A+指向虚基类的指针B,C虽然加入了自己的虚函数,但是虚表指针是和基类共享的,因此不会有自己的虚表指针D由于B,C都是虚继承,因此D只包含一个A的副本,于是D大小就等于A+B中的指向虚基类的指针+C中的指向虚基类的指针

如果B,C不是虚继承,而是普通继承的话,那么A,B,C的大小都是8(没有指向虚基类的指针了)D由于不是虚继承,因此包含两个A副本,大小为16.注意此时虽然D的大小和虚继承一样,但是内存布局却不同。

然后,来看看VC的编译器

vc对虚表指针的处理比GCC复杂,它根据是否为虚继承来判断是否在继承关系中共享虚表指针,而对指向虚基类的指针和GCC一样是不共享,当然也不可能共享。

代码同上。

运行结果将会是sizeof(A)=8,sizeof(B)=16,sizeof(C)=16,sizeof(D)=24.

解释:A中依然是int+虚表指针。BC由于是虚继承因此虚表指针不共享,由于B,C加入了自己的虚函数,所以B,C分别自己维护一个虚表指针,它指向自己的虚函数。(注意:只有子类有新的虚函数时,编译器才会在子类中添加虚表指针)因此B,C大小为A+自己的虚表指针+指向虚基类的指针D由于B,C都是虚继承,因此D只包含一个A的副本,同时D是从B,C普通继承的,而不是虚继承的,因此没有自己的虚表指针。于是D大小就等于A+B的虚表指针+C的虚表指针+B中的指向虚基类的指针+C中的指向虚基类的指针

同样,如果去掉虚继承,结果将和GCC结果一样,A,B,C都是8D16,原因就是VC的编译器对于非虚继承,父类和子类是共享虚表指针的

利用visual studio 命令提示(2008),到xx.cpp 文件目录下 运行cl /d1 reportSingleClassLayoutB xx.cpp

虚继承和虚函数继承_第1张图片

第一个vfptr 指向B的虚表,第二个vbptr指向A,第三个指向A的虚表,因为是虚拟继承,所以子类中有一个指向父类的虚基类指针,
防止菱形继承中数据重复,这样在菱形继承中,不会出现祖先数据重复,而只指向祖先数据的指针

2、虚函数继承

虚继承和虚函数继承_第2张图片

带有虚函数的普通继承,前面一个是父类,后面一个是子类,多态实现只需要更新虚函数表即可.,虚函数指针没有变,变得只是覆盖的虚函数表中的函数,你可以把虚函数指针想象成普通的成员变量,编译的时候自动插入类中,用的是同一个虚函数指针,只不过子类对虚函数表中对应的虚函数进行了更新。

参考:

http://www.baidu.com/search/ressafe.html?q=&ms=3&url=http://wenku.baidu.com/view/8674c848fe4733687e21aa77.html

http://blog.csdn.net/haoel/article/details/1948051/

你可能感兴趣的:(虚继承和虚函数继承)