虚拟继承-钻石问题-到底发生了什么

问题描述 投票:0回答:1

我了解并阅读了足够多的关于通过虚拟继承解决的钻石问题。我的问题是

“将 virtual 放置在您要继承的基类旁边实际上意味着什么”

class A { public: void Foo() {} };
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};

我想知道我的理解是否正确。在声明中

  class D : public B, public C {}

编译器将检查第一个基类 B,并注意到它实际上继承自

class A
。编译器将检查类 A 的实例是否存在,如果不存在,它将创建一个由 B 派生的 A 实例。然后,编译器将检查类 C 并注意到它实际上是从类 A 继承的。但是,由于它是由 C 虚拟继承的,并且 A 的实例已经存在,因此它不会包含新实例。从而解决了钻石问题。这种理解正确吗?

c++ virtual-inheritance
1个回答
1
投票

是的,您的理解是正确的,但很难找到虚拟继承合理的场景,即使在这些情况下,问题通常可以用更简单的方法来解决。

在您的情况下,您的 D 实例将由 C、B 和单个 A 实例组成。由于 A 和 B 都可以参与具有不同组合的不同场景,其中 A 实际上被许多类(甚至可能比 A 和 B 更多的类)继承,因此不清楚如何布局,例如 D 实例的构建块记忆。因此,为 B 类和 C 类编译的代码通常使用实用程序指针或偏移量(每个实例 1 个指针,一个用于 B,一个用于 C),它们指向由继承的不同类的代码共享的 A 实例实际上来自 A 类。这会占用 D 实例中的额外空间,并通过使用这些指针来降低代码的效率。

另一个丑陋的实现细节是,对于 D 类,当您创建/初始化 D 的实例时,您的 B 和 C 实例都在“竞赛”从其构造函数调用类 A 的构造函数。因此,您的 D 实例还将包含一个 bool 变量,该变量指示是否有人已经从 A 的构造函数中调用了 A 的 ctor。 B 和 C 的 ctor 之一将获胜并将此 bool 变量设置为 true,因此来自其他“虚拟后代”的 ctor 的以下尝试在检查该特定 bool 值后只需退出。

虚拟继承非常丑陋,应尽可能避免。

© www.soinside.com 2019 - 2024. All rights reserved.