一直以为自己对多态和继承已经比较了解,当遇到虚继承的时候,发现有点犯晕,想不通了,于是在微博上向几个大神请教,很快得到了他们的回复,高兴之情无以言表。之后自己查了一些资料,结合大神的回复,在这里做一下简单的记录。
我的问题如下:
为什么虚继承类的sizeof要大些啊,是因为虚继承中,子类有指向父类的指针和指向父类的虚函数表的指针吗,比非虚继承多了这两个指针? @左耳朵耗子 @简悦云风 @GeniusVczh
@GeniusVczh:调用的时候给的this和函数实际需要的this的指针不一定是一样的,多重继承的时候已经这样了。再加上你还有virtual继承,所以需要很多描述。
V福尔摩斯 回复 @GeniusVczh:看了B(第三个图中的B)的内存布局,的确有vfptr和vbptr两个指针
简悦云风:和编译器实现有关。实现上虚继承更象是组合,因为它可以被菱形继承而只有一份,所以加上一个额外指针引用这个对象。没有虚函数时不生成虚表,所以 2 里就是 a 对象加额外指针。3里面 b 有虚表,就再加一个虚表指针。字数不够不吐槽了。
左耳朵耗子:1) int a :4字节;2)虚函数增加一个虚表指针:4字节。3)虚继承还会增加一个指针:4字节。但是为什么最后会是16个字呢?你是在用VC++吧?看一下我的这篇文章(http://t.cn/a1lMjd 最后一个示例)你会知道VC++的对象布局是有点诡异。G++下应该是正常的。
看了简悦云风和左耳朵耗子两个大神的回复之后,自己觉得还是有点晕,于是看了一下《Effective C++》和《More Effective C++》,在这里做一下记录。
多态的实现原理:
1:含有虚方法的类都有一个虚函数表
2:子类的虚方法会覆盖父类对应的虚方法
3:含有虚方法的类的每个实例都有一个指向虚方法表的指针,如果虚继承的话可能会有多个
4:根据3中的指针调用虚方法表中对应的虚方法
多态的实现差不多就是上面几点。面试中经常遇到的就是调用哪个方法的问题,一句话告诉你是怎么调用的:在继承关系中,非虚方法调用指针类型的方法;虚方法调用指针所指的对象类型的方法。非虚方法和默认参数都是静态绑定,在继承关系中只跟指针类型有关,跟指针所指的对象的实际类型无关。还有一点就是非虚方法就像C方法一样,不用太在意,证明非虚方法就像C方法的一个方式就是,用一个空指针调用一个非虚方法,只要这个对象没有用到对象的数据,就不会有任何问题。
再来看看我发问的哪个图,为什么图3中sizeof(B)=16,于是用VS自带的工具看了一下B的内存布局,如下图:
查看对象内存布局的VS命令:cl [filename].cpp /d1reportSingleClassLayout[className]
看到这个图,其实还是不太好理解,int a占4字节,B有自己的虚函数表,虚函数指针占4字节,另外多出一个vfptr和vbptr,那就只能这样理解了:vfptr指向父类的虚函数表,B每多虚继承一个类,就多一个vfptr,不信你可以试试,vbptr指向A,但是每多虚继承一个类,并不多出一个vbptr,这是和解呢?
@pop_Atry:干嘛不把B到A的偏移量放到虚表里面,何必为每个对象添加一个额外引用?
@简悦云风:回复@pop_Atry: 1. 性能原因; 2.有的编译器的确是放偏移量的.
风神一语中的,但如果不按照@pop_Atry,就是我们现在看到的,B变大了。
妹的,不同的编译器有不同的实现,咋们讨论这个问题有什么意义呢,千万不要把不同编译器编译的代码放到同一个程序中啊,啊哥。
虚拟继承的出现就是为了解决重复继承中多个间接父类的问题的,保证每个父类都只有一份。
Effective C++的作者建议尽量避免多继承,如果不能避免也要避免菱形继承,各种莫名其妙的复杂啊。所以说多继承也就算了,还许多(虚多)继承,那就要搞死人了。
更多了解请看左耳朵耗子的相关博客:http://blog.csdn.net/haoel/article/details/3081385
本文转自啊汉博客园博客,原文链接:http://www.cnblogs.com/hlxs/archive/2013/05/15/3079302.html