链接:https://www.zhihu.com/question/24858417/answer/495923089
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
在msvc环境下,虚函数和虚继承是分开的。其实非常简单,以下分类讨论。
1.B有虚函数,那么B有一个指针vfptr指向虚函数表,虚函数表里只有两种东西,一个type_info(为了rtti)和所有虚函数的地址。
2.D继承B,那么D把B的vfptr和虚函数表也继承,然后修改type_info,D中override的函数也在虚函数表中进行改写。
3.若是多重继承,那么子类会有多个vfptr。假设B1,B2都有虚函数,且为D的父类。那么B2* b2 = d;的时候,程序会转化成B2* b2 = (B2*)((char*)d + sizeof(B1));所以不用担心找错或者找不到表。
以上是对虚函数且非虚继承的讨论。
4.B没有虚函数,D虚继承B,那么D有一个指针vbptr指向一个虚基类表,虚基类表里存放的是虚基类B的初始位置和子类D的vbptr的位置差。
5.若是多重虚继承,即D虚继承B1,B2,那么虚继承表里有多个表项。
6.若是嵌套virtual,即D2虚继承D1,D1虚继承B,那么同样的,D2有一个vbptr指向虚基类表,这个表存储D1的初始位置和D2的vbptr位置差,同时为了防止嵌套层次增多而引起的对B中对象的访问时间的增长,这个表同时也存储B的初始位置和D2的vbptr的位置差。即,这个虚基类表有两个表项。
以上是对虚继承且没有虚函数的讨论。
题主好奇的是两者混合的情况,这就很有意思了,尤其是题主举的最后一个例子,非常有意思。我初学的时候也想过,得到了非常有意思的结论。我们接着说。
7.若虚基类没有虚函数,显然两者不会有交集,各管各的。
8.若虚基类有虚函数。即B中有虚函数f。
8.1.那么,D1虚继承B,且D1中没有新的虚函数(即,D1中的所有虚函数都是override虚基类B的),那么D1继承B的vfptr,这时就有一个问题。若D2也虚继承B,还没问题。若D3继承D1和D2,那么D3只有一个vfptr和一个虚函数表。那么若D1和D2都改写了f,那么D3就炸了。D3::f出现了矛盾。所以如果D1和D2都改写了f,D3不能同时继承D1和D2。若D1或D2其中只有1个或0个改写了f,则D3可以继承,而且非常有意思的是,若D1改写了f,D2* d2 = d3,那么d2->f()调用的是D1::f,尽管D1和D2没什么关系。
8.2.若D虚继承B,且D中有新的虚函数,那么,有趣的是,D除了有B虚继承来的vfptr之外,还有一个新的,属于D自己的vfptr,即D的对象模型中有两个vfptr。而这两个vfptr分别管理两组虚函数,继承来的B的vfptr管理B中就有的虚函数,而新的vfptr管理新的虚函数。这就解释答主的问题了。
完。
其实msvc这套实现非常简(xiao)洁(lv)易(di)懂(xia),比g++两表合一容易理解多了。
浙公网安备 33010602011771号