3

我明白从基类派生时使用虚拟关键字的要求,以避免与钻石继承相关的模糊问题。为什么'虚拟'继承不是默认行为?

但是,我的问题是为什么这不是C++中的默认行为,当派生类,而不管钻石问题可以或不可能在那里?

在钻石继承不存在的情况下,使用'虚拟'关键字有什么'伤害'?

+1

伤害是在使用'virtual'关键字的地方创建了钻石继承,但这是不需要的。考虑一个银行账户,它是一个“DollarPaymentEndpoint”和一个“YenPaymentEndpoint”,其中每个账户都从具有“AmountReceived”成员的“PaymentEndpoint”继承。 –

+2

[为什么需要virtual关键字?](http://stackoverflow.com/questions/13600934/why-is-the-virtual-keyword-needed)讨论了为什么'virtual'不是自动的。 –

+3

@DavidSchwartz我的错误。默认情况下具有虚拟继承会导致意外共享并破坏封装。它也可以防止课程成为POD。 –

回答

4

有额外的开销,试试吧:

#include <iostream> 

struct Foo { 
    int a; 
}; 

struct Bar : Foo { 
    int b; 
}; 

struct Baz : virtual Foo { 
    int b; 
}; 

int main() { 
    std::cout << sizeof(Foo) << " "; 
    std::cout << sizeof(Bar) << " "; 
    std::cout << sizeof(Baz) << "\n"; 
} 

在我执行我得到4 8 16。虚拟继承需要vptr或等效机制,因为类Baz不知道Foo基类子对象相对于Baz基类子对象将在多大的偏移量处出现偏移。这取决于最衍生类型是否也通过其他路线继承Foo

由于vptr的是存在的,一个还预计在某些情况下,将被使用,这是更开销:-)也就是说,一个或多个额外的间接寻址,以便经由一个或Baz*Baz&访问Foo::a必需的。如果编译器以某种方式知道referand的派生类型最多,那么编译器可能会选择避免这种情况。

5

虚拟继承有一个运行时开销:转换指针需要一个只在运行时已知的调整,而对于非虚拟继承,它可以在编译时知道。它也可以使类更加复杂,因为虚拟基类由最终派生类初始化,而不是(必然)直接从它们继承的类。

因此,只有当你特别需要钻石结构时才需要它;记住指定非虚拟继承以避免隐藏的开销会是一件痛苦的事情。 C++通常遵循不应该为不需要的功能付费的原则。