我理解从基类派生时使用 virtual 关键字的要求,以避免与菱形继承相关的歧义问题。
但是,我的问题是为什么在派生类时这不是 C++ 中的默认行为,无论是否存在钻石问题?
在不存在钻石继承的情况下使用“虚拟”关键字是否有任何“危害”?
我理解从基类派生时使用 virtual 关键字的要求,以避免与菱形继承相关的歧义问题。
但是,我的问题是为什么在派生类时这不是 C++ 中的默认行为,无论是否存在钻石问题?
在不存在钻石继承的情况下使用“虚拟”关键字是否有任何“危害”?
虚拟继承有运行时开销:转换指针需要调整,只有在运行时才知道,而对于非虚拟继承,它可以在编译时知道。它还可以使派生类更加复杂,因为虚拟基类由最终派生类初始化,而不是(必然)直接从它们继承的类。
因此,只有在您特别想要钻石结构时才需要它;必须记住指定非虚拟继承以避免隐藏的开销会很痛苦。C++ 通常遵循不为不需要的功能付费的原则。
有开销,试试看:
#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 在那里,人们还期望在某些情况下会使用它,这会增加开销 :-) 也就是说,需要一个或多个额外的间接寻址才能Foo::a
通过 aBaz*
或访问Baz&
。如果编译器以某种方式知道引用的最派生类型,则编译器可能会选择避免这种情况。