作者:匿名用户
链接: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++两表合一容易理解多了。

posted on 2021-04-04 00:04  逆流而上の鱼  阅读(398)  评论(0)    收藏  举报