【问题标题】:C++, diamond inheritance, where/when do pure virtuals need to be implemented?C++,钻石继承,何时/何时需要实现纯虚拟?
【发布时间】:2011-11-18 19:27:40
【问题描述】:

C++:我有一个带有纯虚函数 f() 的基类 A,然后两个类 B 和 C 虚拟继承自 A,还有一个类 D 继承自 B 和 C(典型的菱形结构):

   A f() = 0
 v/ \v
 B   C
  \ /
   D

下列情况下,何时何地需要实现f() = 0?

  1. B 和 C 也有纯虚函数(-> 抽象类是否必须实现继承的纯虚函数?)
  2. 其中只有一个(B XOR C)具有纯虚函数(-> 另一个是否仍必须实现 f()?)
  3. B 和 C 都没有自己的纯虚拟(-> 跳过 B 和 C 中的实现并将其“传递”给 D 的可能方法?)
  4. 在以上三种情况下,D需要实现f()吗?在哪些情况下 D 可以选择实现 f()?在哪些情况下,如果有的话,D 不可能实现 f()?

对于这类问题还有其他常见的建议吗?

谢谢。

【问题讨论】:

  • @Tux-D 什么?对于我正在从事的现实项目,这可能是一个可能的案例。我仍在考虑几种方法。
  • 我通常认为菱形继承是糟糕的设计。当然有可能,但通常有更好的方法。多重继承最好使用纯虚拟基类。
  • 我同意 AJG85。如果你有菱形继承模式,你应该重新考虑你的设计。
  • "我通常认为菱形继承是糟糕的设计。" 为什么? “带有纯虚拟基类。”那是什么?

标签: c++ inheritance multiple-inheritance pure-virtual


【解决方案1】:

B 和 C 也有纯虚函数(-> 抽象类必须实现继承的纯虚函数吗?)

D必须实现ALL继承的纯虚函数。
除非一个类实现了类的所有纯虚函数,否则它派生自类本身就充当抽象类。

其中只有一个(B XOR C)具有纯虚函数(-> 另一个是否还必须实现 f()?)

D 必须实现它通过其基类在任何层次结构中继承的纯虚函数。如果它的直接基类没有定义一个纯虚函数,那么这个类也变成了一个抽象类,除非D 实现了继承的纯虚函数,它也会变成抽象类。

B 和 C 都没有自己的纯虚(-> 跳过 B 和 C 中的实现并将其“传递”给 D 的可能方法?)

D 必须实现它通过A->BA-C 继承的纯虚函数。注意在这种情况下BC 都将是抽象类。

在上述三种情况下,D需要实现f()吗?在哪些情况下 D 可以选择实现 f()?在哪些情况下,如果有的话,D 不可能实现 f()?

D 需要在上述所有 3 个条件中实现 foo(),才能实例化(非抽象)。

结论:

  1. 一个类需要实现它从它的所有基类继承的所有纯虚函数,否则会使该类成为抽象类。
  2. 虚拟属性是继承的,如果超类声明了一个虚拟函数,那么派生类中被覆盖的函数也是虚拟的,它会将虚拟属性传递给从它派生的所有类。

避免死亡之钻!除非你真的了解其中的微妙之处。很多人尝试使用虚拟继承,但实际上它并不是实现他们的设计想要实现的目标的最恰当方式。在某些情况下确实需要使用虚拟继承,但它仍然是语言提供的重要构造,但更多时候以错误的方式使用。因此,重新审视您的设计以验证您是否真的需要虚拟继承是有意义的。

以下可能是一个很好的阅读:

  1. Multiple Inheritance - Part I
  2. Multiple Inheritance - Part II
  3. Multiple Inheritance - Part III

【讨论】:

  • 我说得对:如果 A 有一个纯虚函数,而 B 和 C 都没有实现它,那么它们也自动抽象了?
  • @Mahesh 如果 B 和 C 都实现了纯虚函数,那么 D 还必须实现吗? Id 不继承 B 或 C 的实现之一?
  • @bluebernd:D 需要有一个“唯一的最终覆盖”,无论你以何种方式实现它。
  • 如果A 有一个纯虚,D 必须实现它,不管BC 做什么。如果BC(或两者)都无法实现它,D 将是抽象的而不实现它。如果BC都实现了,则D中的功能不明确; D 必须实现它以消除歧义。
  • 一个很好的答案直到我们得到以下线。虚拟继承是一种工具,就像任何其他工具一样,菱形继承在许多情况下都是有效的。 Mixins 很明显,但其他情况请参见 Barton & Nackman。
【解决方案2】:

B 和 C 也有纯虚函数(-> 抽象类必须实现继承的纯虚函数吗?

是的。从抽象类继承的任何类必须实现虚函数才能实例化。否则它们也将是抽象的。

其中只有一个(B XOR C)具有纯虚函数(-> 另一个是否还必须实现 f()?)

由于C也派生自A,它也应该实现f()

B 和 C 都没有自己的纯虚(-> 跳过 B 和 C 中的实现并将其“传递”给 D 的可能方法?)

你可以这样做。但这会阻止 BC 单独实例化 -

A *obj = new B(); // Error
A *obj = new C(); // Error

在上述三种情况下,D需要实现f()吗?在哪些情况下 D 可以选择实现 f()?在哪些情况下,如果有的话,D 不可能实现 f()?

f() 可以单独在 D 中实现,只要您确实希望它的所有父类都是抽象的。

【讨论】:

  • 不过,OP 从来没有说他是否真的要实例化 BC
  • @Kerrek SB - 提到了一个问题。
【解决方案3】:

唯一的要求是,在“叶”级别(最衍生的),所有东西都有一个实现(可能只有一个,否则如果 - 在您访问图表时 - 在某个位置有更多实现,则会出现歧义)相同的“距离”。

所以 D 必须实现所有尚未实现的内容,或者所有内容都通过不同的路径多次实现(以消除歧义)。 如果在 D 级别的某些东西仍未实现... D 不能被实例化,并且需要从 D 派生 E 来实现。

- 钻石是永恒的 -

【讨论】:

    【解决方案4】:
    1. 不,抽象类(大概是BC)不需要实现继承的纯虚拟。不过,像D 这样的子类需要这样做才能被实例化。

    2. 不,BC 将继承纯虚方法,只需要重写它即可生成最终的具体类。

    3. 是的,这将以两种方式之一通过实现传递给D。如果BC 都虚拟继承,那么D 将实现一次。如果它们没有虚拟继承,那么D 将需要覆盖bothfBC 版本。

    4. 如果D 是抽象的,它永远不需要实现f。假设您的意思是具体的,在所有三种情况下,您都需要在D 中覆盖f。只有当BC 都覆盖fD 才不需要。

    仔细查看您的设计并删除钻石继承。在大多数情况下,这将是防止所有这些问题的最佳选择。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-02
      • 2020-01-01
      • 2012-11-05
      • 2017-12-09
      • 2012-04-19
      • 2011-02-09
      相关资源
      最近更新 更多