7

想象一下标准的钻石继承。A 类定义了纯虚函数 fx,B 类定义了 fx 的实现,C 类和 D 类对 fx 没有任何作用。当尝试在 D 类的实例上调用 fx 时,您将收到“模糊函数调用”错误,尽管 fx 只有一种实现。这可以通过 B 和 C 以虚拟方式从 A 继承来解决。这是解决问题的正确方法吗?虚拟继承究竟如何处理虚函数表的合并?

A--->B--->D

\--->C--------^

4

2 回答 2

19

... 注意,Herb Sutter 写了 3 篇关于多重继承的优秀文章(1) here(2) here(3) here他在这里的本周大师中写了一大堆有用的文章。强烈推荐 ...

首先,我不确定我的层次结构是否正确。我认为它是这样的:

struct A {
    virtual void F() = 0;
};

struct B : A { void F() { } };
struct C : A { };
struct D : B, C { };

A嗯,D 是抽象的,因为在 D 类型的对象中有两个子对象:一个B通过 B 的格子变得具体,另一个在通过的格子中仍然是抽象的C。我认为你有一个指针D并尝试调用F. 是的,出现了歧义,因为编译器F在两个单独的格中找到了两个函数:

D -> B::F
D -> C -> A::F

看起来像这样:

    F()   F()
     A     A
     |     |
 F() B     C
      \   /
        D 

您可以通过虚拟地从 A 派生来正式解决这种情况:

struct B : virtual A { void F() { } };
struct C : virtual A { };
struct D : B, C { };

然后你有这种情况,称为钻石继承:

       F()  
        A
      /   \
 F() B     C
      \   /
        D 

并进行查找,它发现有B::F覆盖A::F。虽然A::F仍然可以通过 到达D::C::A,但这不再是一个歧义,因为A是继承的 virtual。

这是否是您特定问题的正确解决方案 - 当然不确定。通常有比从类派生虚拟更好的方法。关于合并虚函数表的问题 - 这完全取决于实现。,据我所知,如果我们派生虚拟GCC,它将在 的虚拟表中保留一个指向 A 实例的指针。D

于 2009-01-19T14:02:51.493 回答
3

这是解决问题的正确方法吗?

“钻石”继承是有问题的,解决方案的正确解释需要一点解释。我建议您阅读 Meyers 的Effective C++的以下章节:

  • 第 26 条,防范潜在的歧义
  • 第 43 条,明智地使用多重继承
于 2009-01-19T13:58:34.043 回答