与继承虚拟功能不同,解析虚拟继承看起来很干脆但也许我不够有创意(狡猾?)。
虚拟继承是否与虚函数的继承有关?具体来说,虚拟继承是否会引发后期绑定?我看不出任何理由。由于关键字过载,我只是怀疑。
我意识到标准没有指定虚拟继承的实现。我对大多数非假想的机器都感兴趣,但不完美。
答案 0 :(得分:5)
正如虚函数涉及这些成员函数的后期绑定一样,我想你可以说虚拟继承涉及继承数据成员的后期绑定。每个子类的内存布局可能有很大不同,因此如果没有运行时类型信息,则无法解析baseClassInstance->dataMember
这样的表达式。因此virtual
的两种用法都需要使用" vtables"用于特定于类的查找。
请参阅"Memory Layout for Multiple and Virtual Inheritance", by Edsko de Vries,了解GNU编译器集群(gcc)如何实现虚拟继承,包括对象布局,后果等。据我所知,其他编译器在关键点。
答案 1 :(得分:1)
虚拟继承并非没有运行时成本,但是这种成本的原因不是增加灵活性,而是解决模糊性。
以多继承层次结构为例,其中类C
通过不同的基类继承了类A
两次。对类型为A::foo
的对象的非静态方法C
的调用现在是不明确的(无论该调用是否为虚拟调用)。问题是传递给成员函数的隐式this
指针。通常,内存中每个子类的位置由继承层次结构唯一确定,但在这种情况下,由于A
中包含两次C
,编译器必须决定如何调整this
成员函数调用的指针 - 它不能单独执行,因此它会要求您决定。
此决定更加复杂,因为我们不仅可以通过A::foo
调用C
,还可以通过C
的基类调用this
。这造成了两难:根据我们用于调用的基类,编译器将以不同的方式调整A
指针,将我们重定向到内存中A
的不同位置,具体取决于我们指针类型用于通话。实际上,对于C
的每个实例,我们在内存中有两个不同的class A {
public:
void foo();
[...]
};
class B1 : public A {};
class B2 : public A {};
class C : public B1, public B2 {};
C c;
B1* b1 = &c;
B2* b2 = &c;
//assume foo() changes some internal state of A
b1->foo();
//the state change of the previous line is not visible
//to the next call - they operate on distinct instances of A
b2->foo();
实例。
virtual
A
继承引入了一个额外的间接层来解决这种歧义。不是在编译时确定A::foo
相对于其子类的位置,而是执行运行时查找。这允许编译器传递相同的内存位置调用C
,无论通过哪个派生类进行调用。对于A
的每个实例,我们现在只在内存中有一个class B1 : virtual public A {};
class B2 : virtual public A {};
[...]
C c;
B1* b1 = &c;
B2* b2 = &c;
//both calls will now operate on the same instance of A
//state changes performed by the one will be observed by the other
b1->foo();
b2->foo();
实例。
{{1}}