使用多重继承时如何避免死亡钻石?

新手上路,请多包涵
阅读 345
2 个回答

一个实际的例子:

 class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};

注意 D 类是如何从 B 和 C 继承的。但是 B 和 C 都从 A 继承。这将导致类 A 的 2 个副本包含在 vtable 中。

为了解决这个问题,我们需要虚拟继承。需要虚拟继承的是 A 类。因此,这将解决问题:

 class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};

原文由 Mark Ingram 发布,翻译遵循 CC BY-SA 2.5 许可协议

这就是我在关于这个主题的笔记中的全部内容。我想这会对你有所帮助。

菱形问题是当两个类 B 和 C 继承自 A,而类 D 继承自 B 和 C 时出现的歧义。如果 A 中有一个成员 B 和 C,并且 D 没有覆盖它,那么哪个成员D 继承:B 的继承,还是 C 的继承?

 struct A { int a; };
struct B : A { int b; };
struct C : A { int c; };
struct D : B, C {};

D d;
d.a = 10;       //error: ambiguous request for 'a'

在上面的例子中,B 和 C 都继承了 A,并且它们都有一个 A 的副本。但是 D 继承了 B 和 C,因此 D 有两个 A 的副本,一个来自 B,另一个来自 C。如果我们需要通过 D 的对象访问 A 的数据成员 an,我们必须指定访问该成员的路径:无论是来自 B 还是来自 C,因为大多数编译器无法区分 D 中 A 的两个副本。

有 4 种方法可以避免这种歧义:

1-使用范围解析运算符我们可以手动指定访问数据成员的路径,但请注意,在 D 中仍然存在 A 的两个副本(两个单独的主题),因此仍然存在问题。

 d.B::a = 10; // OK
d.C::a = 100; // OK
d.A::a = 20; // ambiguous: which path the compiler has to take D::B::A or D::C::A to initialize A::a

2- 使用 static_cast 我们可以指定编译器可以通过哪条路径到达数据成员,但请注意,在 D 中仍然有 A 的两个副本(两个单独的子对象),所以仍然存在问题。

 static_cast<B&>(static_cast<D&>(d)).a = 10;
static_cast<C&>(static_cast<D&>(d)).a = 100;
d.A::a = 20; // ambiguous: which path the compiler has to take D::B::A or D::C::A to initialize A::a

3- 使用覆盖,模糊类可以覆盖成员,但请注意,在 D 中仍然存在 A 的两个副本(两个单独的子对象),所以仍然存在问题。

 struct A { int a; };
struct B : A { int b; };
struct C : A { int c; };
struct D : B, C { int a; };

D d;
d.a = 10;    // OK: D::a = 10
d.A::a = 20; // ambiguous: which path the compiler has to take D::B::A or D::C::A to initialize A::a

3-使用虚拟继承,问题彻底解决:如果A到B的继承和A到C的继承都标记为“virtual”,C++特别注意只创建一个A子对象,

 struct A { int a; };
struct B : virtual A { int b; };
struct C : virtual A { int c; };
struct D : B, C {};

D d;
d.a = 10;    // OK: D has only one copy of A - D::a = 10
d.A::a = 20; // OK: D::a = 20

注意B和C 必须是虚拟的,否则如果其中一个是非虚拟的,D就会有一个虚拟的A子对象和另一个非虚拟的A子对象,即使类D本身仍然会产生歧义是虚拟的。例如,D 类在以下所有内容中都是模棱两可的:

 struct A { int a; };
struct B : A { int b; };
struct C : virtual A { int c; };
struct D : B, C {};

Or

struct A { int a; };
struct B : virtual A { int b; };
struct C : A { int c; };
struct D : B, C {};

Or

struct A { int a; };
struct B : A { int b; };
struct C : virtual A { int c; };
struct D : virtual B, C {};

Or

struct A { int a; };
struct B : virtual A { int b; };
struct C : A { int c; };
struct D : virtual B, C {};

原文由 mada 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题