http://en.wikipedia.org/wiki/Diamond_problem
我知道这意味着什么,但我可以采取哪些措施来避免它?
原文由 ilitirit 发布,翻译遵循 CC BY-SA 4.0 许可协议
http://en.wikipedia.org/wiki/Diamond_problem
我知道这意味着什么,但我可以采取哪些措施来避免它?
原文由 ilitirit 发布,翻译遵循 CC BY-SA 4.0 许可协议
这就是我在关于这个主题的笔记中的全部内容。我想这会对你有所帮助。
菱形问题是当两个类 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 许可协议
3 回答2k 阅读✓ 已解决
2 回答3.9k 阅读✓ 已解决
2 回答3.2k 阅读✓ 已解决
1 回答3.2k 阅读✓ 已解决
1 回答2.7k 阅读✓ 已解决
3 回答3.4k 阅读
1 回答1.6k 阅读✓ 已解决
一个实际的例子:
注意 D 类是如何从 B 和 C 继承的。但是 B 和 C 都从 A 继承。这将导致类 A 的 2 个副本包含在 vtable 中。
为了解决这个问题,我们需要虚拟继承。需要虚拟继承的是 A 类。因此,这将解决问题: