我知道虚拟继承的用法:
class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};
我想知道的是
之间的区别class X { public: void Bar() {} };
class Y : public virtual X {};
和
class X { public: void Bar() {} };
class Y : public X {};
主要从实施角度出发。我的意思是什么时候我不想使用虚拟继承?
答案 0 :(得分:1)
首先,虚拟继承将所有相同类型的虚拟基础“合并”到单个基础子对象中,由完整对象中的所有派生类型共享。你想要那样吗?如果您希望这些基数由单独的基础子对象表示,那么您就不能使用虚拟继承。所以,你的意图很明显。
在您的第一个示例中,同一个A
子对象将由类B
中完整对象中的类C
,D
和D
共享。如果继承是非虚拟的,那么B
将拥有自己独立的A
,C
将拥有自己独立的A
。这显然会影响依赖于A
对象标识的任何代码的行为。
所以,从这个角度来看,这真的是一个问题:你想要实现什么?一个共享的基础子对象?还是多个独立的基础子对象?
(您的X
和Y
示例是非代表性的。虚拟继承的上述属性仅在多继承配置中显示出来。在单继承情况下,虚拟继承除了强加潜力外什么都不会实现性能/空间开销。)
其次,为了提供从所有派生类到单个共享基类子对象的访问(在虚拟继承的情况下),编译器将使用运行时实现结构。通常,派生类将通过使用虚拟基础嵌入到每个对象中的指针来访问其虚拟基础。这会降低性能,并且需要在对象的布局中留出额外的空间。
同时,“正常”(非虚拟)继承产生固定的编译时布局,这意味着类之间的所有转换都是通过添加或减去编译器立即知道的固定偏移量来执行的。这样更快,不需要额外的运行时信息。
答案 1 :(得分:1)
不同之处在于将生成虚拟继承类的 Vbtable 。您可以在this article:
中了解相关信息为多个虚拟继承生成vbtable(虚拟基类表)。因为有时需要向上类(转换为基类),所以需要确定基类的确切位置。 vbtable包含每个基类'vftable的位移,它实际上是派生类中基类的开头。
答案 2 :(得分:0)
其他答案都很好,请先阅读。
现在,让我添加虚拟继承:
class X { public: void Bar() {} };
class Y : public virtual X {};
您将无法从X
引用封闭Y
:
Y y;
X &rx = y; // refers to the X base class subobject
Y &ry = ??? (rx); // how to get back a reference to y from rx
无法从Y
rx
的引用
static_cast<Y&> (x)
,因为它仅适用于非虚拟继承dynamic_cast<Y&> (x)
,因为它仅在X
具有某些虚拟功能但你要做的就是添加一些虚函数:
class X {
public:
void Bar() {}
virtual ~X () {}
};
最好为一个设计为派生类的类提供虚拟析构函数。这个一般规则的例外情况很少(主要是带有受保护的析构函数的类)。
答案 3 :(得分:-1)
虚拟继承的用法是处理多重继承,如下所示
在你的第二种情况下,从A调用(虚拟)方法或函数的子类D不知道它是通过B还是通过C调用它。通过虚拟继承,两个“路径被一起提取”。 / p>