【问题标题】:Is Multiple Inheritance that bad in this particular scenario?在这种特定情况下,多重继承有那么糟糕吗?
【发布时间】:2020-10-07 03:37:02
【问题描述】:

目前我正在尝试理解 MI 的“邪恶”。我刚刚在 youtube 上观看了一个视频,其中一个 js 家伙反对继承。这是他的例子(我已经用 C++ 重写了):

struct Robot
{ void drive(); };

struct MurderRobot : public Robot
{ void kill(); };

struct CleanerRobot : public Robot
{ void clean(); };

struct Animal
{ void poop(); };

struct Dog : public Animal
{ void bark(); };

struct Cat : public Animal
{ void meow(); };

然后他建议了一个新类MurderRobotDog,在他看来,这不能通过继承来优雅地完成。当然,它不能通过单继承来完成。但我认为用 MI 做这件事没有任何问题。

我认为我们可以创建一个基类BarkingObject,它会包含所有吠叫的东西。然后 Dog 继承自 Animal ,它具有共同的 poop() 和 BarkingObject。而当你需要一个杀狗机器人时,它必须从BarkingObjectMurderRobot 继承。这更有意义。 MurderRobotDog 不能从活的生物继承,因为这样它就会变成活的,这与机器人的定义相矛盾。当然,为此您必须使用被许多人认为是 EVIL 的多重继承。不幸的是,如果没有它,我们似乎无法有效地重用不同的不相关(你不需要 poop() 来进行 bark(),并且机器人案例证实了这个断言)功能。

你反对我的建议的理由是什么?

【问题讨论】:

  • 当然你可以用多重继承写一个MurderRobotDog。多重继承的问题不在于它是“邪恶的”,问题在于它带来了当你不使用它时根本没有的并发症
  • 它甚至不是多重继承使事情变得复杂,而是常见的基础和名称冲突。除非这种设计有特定原因,否则请避免它。在一些其他玩具示例的背景下谈论这个是没有意义的。
  • @OleksijPlotnyc'kyj 首先,是的。你知道虚拟继承吗?只是另一个需要推理和决定的维度。最好让事情尽可能简单。这并不意味着 MI 本质上是邪恶的。如果它在你的设计中有意义,那就去吧。例如,我认为 iostreams 是一个非常有意义的示例。
  • fwiw,称某事为“邪恶”从未帮助任何人。著名的“goto is evil”是一个错误的引述,原标题是“go to statement被认为有害”,并附有对为什么它被认为有害的深入解释。您需要知道为什么不使用它以及有什么缺点,只有这样您才能做出有根据的决定是否使用它。我的建议:不要相信任何称某事为“邪恶”的人
  • “如果没有它,我们将无法有效地重用 [...] 功能 [MI]” - 看起来更像是想象力的失败,而不是事实陈述。此外,我不确定从多个基类继承一堆杂乱且可能相互冲突的东西,而不是包含它们并明确给定名称/行为的来源有什么效率。

标签: c++ class oop inheritance multiple-inheritance


【解决方案1】:

多重继承实现是解决这类问题的一种老式方法。

作曲是新的方式。

您定义描述特定行为或一组行为的接口

struct Murderer
{
    virtual ~Murderer() = default;
    void kill();
};

struct Pooper
{
    virtual ~Pooper() = default;
    void poop();
};

实际的事物,例如猫、狗或机器人,会相应地继承(即实现)这些接口。在进行适当的操作之前,您可以使用dynamic_cast 或类似的运行时技术来查询对象的接口。

【讨论】:

  • 这与作曲有什么关系?
  • 可能指的是这样的东西:en.wikipedia.org/wiki/Composition_over_inheritance(不要简单地忽略顶部的警告:混淆)。当您以建议的方式实现这些接口时会发生组合。
  • 大多数会采用“组合”来表示“派生”对象包含其他类的实例(值或引用),并且可以通过这种方式访问​​它们的行为。我不是软件架构专家,但之前没有听说过它使用 re 接口。我也没有意识到推荐/现代必须dynamic_cast,真的!
  • @Timbo 那篇文章确实令人困惑,因为在它似乎支持您的观点之前,它首先将组合定义为“类应通过其 组合实现多态行为和代码重用的原则(通过包含实现所需功能的其他类的实例)而不是继承”。从接口继承仍然是继承(尽管可能陷阱更少)。
  • 是的,dynamic_cast 非常慢,需要 RTTI。我的印象是,如果可能,大多数 C++ 开发人员倾向于避免多态性,或者将其用于很少被调用的高级代码。相反,如果可能,模板和概念更常见,更可取 IMO。
猜你喜欢
  • 2015-08-09
  • 1970-01-01
  • 1970-01-01
  • 2014-05-05
  • 1970-01-01
  • 1970-01-01
  • 2014-12-13
  • 1970-01-01
  • 2020-09-26
相关资源
最近更新 更多