我正在研究C++继承,所以我编写了以下代码:
// 08 Diamond Inheritance 02.cpp : Defines the entry point for the console application.
// I'm using Microsoft Visual Studio 2015
#include <iostream>
using namespace std;
class SuperVirtual
{
public:
int sv;
SuperVirtual(int p1 = 11) : sv(p1)
{
cout << "\n SuperVirtual ctor for &" << this;
}
virtual void methodSuperVirtual_01()
{
cout << "\n Inside SuperVirtual::methodSuperVirtual_01(): sv = " << ++sv;
}
virtual void methodSuperVirtual_02()
{
cout << "\n Inside SuperVirtual::methodSuperVirtual_02(): sv = " << ++sv;
}
};
//----------------------------------
class DerivedSV_01 : public SuperVirtual
{
public:
int dsv;
DerivedSV_01(int p1 = 21) : dsv(p1)
{
cout << "\n DerivedSV_01 ctor for &" << this;
}
virtual void methodSuperVirtual_01() override
{
cout << "\n Inside DerivedSV_01::methodSuperVirtual_01()";
}
//----------------------------------------------------
virtual void onlyForDerivedSV_01()
{
cout << "\n Inside DerivedSV_01::onlyForDerivedSV_01()";
}
};
int main()
{
SuperVirtual sv1(1);
sv1.methodSuperVirtual_01();
DerivedSV_01 dsv_01;
dsv_01.methodSuperVirtual_01();
dsv_01.onlyForDerivedSV_01();
return 0;
}我读过这篇文章:
每当类本身包含虚拟函数或重写父类的虚拟函数时,编译器将为该类构建一个vtable。vtable包含指向该类中的虚拟函数的函数指针。每个类只能有一个vtable,而同一个类的所有对象都将共享同一个vtable。
此时,我假设类SuperVirtual的虚拟表包含2个元素,而DerivedSV_01类的虚拟表包含3个元素:2个用于继承(并最终被卵巢覆盖)方法+1用于新的虚拟方法onlyForDerivedSV_01()。
但是,在使用Visual检查main()中创建的对象时,我发现:
SuperVirtual的虚拟表包含两个元素(OK !)DerivedSV_01的虚拟表包含两个元素(,为什么?)为了信息的完整性,我提供以下截图:

发布于 2020-02-10 13:09:14
在调试模式下编译(编译器优化关闭)时,我可以在VS2019中准确地使用您的代码来再现这一点。我确信这只是调试器窗口中的一个显示问题。

请注意,__vfptr数组仅显示在SuperVirtual下,而不是直接显示在dsv_01下,因此调试器当然只知道SuperVirtual vtable中的函数。
但是,如果您查看__vfptr的__vfptr列,您会注意到在SuperVirtual::'vftable'[3]和DerivedSV_01::'vftable[4]' (都标记为绿色)中给出了不同的数组大小。
让我们看看vftable上的内存(vftable的地址可以在Value列的开头看到,用于__vfptr条目,并标记为橙色和红色)。


您将注意到SuperVirtual::'vftable'[3]有两个函数指针(标记为褐色)和一个nullptr。DerivedSV_01::'vftable[4]'有三个指针(标记为紫色和蓝色)和一个nullptr。调试器窗口告诉我们前两个条目是什么(也标记为紫色),但是查看一下“监视”窗口中的第三个条目(标记为蓝色)。

调试器说第三个条目是DerivedSV_01::onlyForDerivedSV_01。这完全符合你(和我)的期望。
发布于 2020-02-10 11:33:22
答案很简单,“就像”规则起作用了。
编译器指出,onlyForDerivedSV_01从未以多态方式使用(因为没有DerivedSV_01的子类),因此它被转换为常规方法。
因此,您只有两个继承表单基类的虚拟方法。
实际上,在完全优化的情况下,编译器应该能够删除对此代码的所有虚拟调用。在这里你可以看到所有方法都直接放在main函数中,只有虚拟调用才能在std::cout上执行。
https://stackoverflow.com/questions/60149330
复制相似问题