我需要一个何时使用非虚拟多重继承的简单示例

I need a simple example of when to use non-virtual multiple inheritance

我教 C++,我需要一个好的、简单的日常示例来说明何时使用它:

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

这里D继承了A两次,因为不涉及虚继承

我需要一个易于理解的激励示例,说明您何时可能需要此构造。我什至不需要代码;我只需要能够向我的学生展示一个例子,让他们惊呼,"Yes, I can see that that would be useful!"

我建议将此示例用于虚拟继承和 non-virtual 多重继承:

A --> Person (id, name)

B --> 老师(工资)

C --> 学生(分数)

D --> 助教

对我来说,AssistantTeacher 是 freshly-graduated 教授和准备更高学位(硕士或博士学位)的老师。他有薪水,也有分数。

要添加 non-virtual 风格,假设教师有特定格式的 ID,学生有其他格式的 ID。 AssistantTeacher有两个id:一个teacher id(在HR数据库中用作主键)和一个student id(在学术系统数据库中用作PK)。

[编辑]

例如,HR ERP 系统会为教师提供一个 auto-increment id,而学术学生系统会为学生提供以下格式的 id:YYYYMMSAAAA(其中 Y 是学生入学的年份, M 是月份,S 是性别:0 或 1,A 是 auto-increment id 部分)。

AssistantTeacher 在 HR 系统中的 ID 可能是“1332”,学生 ID 可能是“20170100004”。

简短版本:多重non-virtual菱形配置继承从不 有用.


更长的版本: 您在此处描述的情况称为 the diamond problem。这正是虚继承要解决的问题

虽然我理解你的问题的意思,但如果语言中存在某种东西,那肯定是出于某些原因存在的。它至少在非常小范围的情况下有用。但我更愿意这样看情况:

  • C++ 中存在多重继承 (MI),对于将多个接口组合在一个中很有用
  • 虚拟 MI 存在于 C++ 中,可用于解决钻石问题
  • Non-virtual 菱形配置中的 MI 仅作为 side-effect 的 MI 存在,是不受欢迎的。

至于虚拟 MI,我喜欢使用分离接口和 implementation-hierarchies(或扩展接口)的示例。例如,当你有这样的配置时:

这不是一个简单的问题,因为任何关于它的讨论都会导致继承与组合。

虚继承的例子,我留着你的棋例

                     Chess_piece
                      attributes: pos_x, pos_y;
                      / \
    _________________/   \___________________
   /                                         \
Bishop                                      Rook
 methods:                                    methods:    
  first_diagonal_move(length)                 horizontal_move(length)
  second_diagonal_move(length)                vertical_move(length)
  \__________________     ___________________/
                     \   /
                    Queen

并且Queen继承了Rook和Bishop的方法,但是Queen只有一个单片的属性,所以我们这里使用C++虚拟继承。

接下来假设我们构建一个由一个圆和一个点组成的复合几何图形。这是一个 "inheritance" 图表:

Point                             Point
 attributes: pos_x, posy           attributes: pos_x
    |                                 |
    |                                 |
Circle                                |
 attribute: radius                    |
     \___________     ________________/
                 \   /
                 Figure

这里的图形将有两个不同的 Point 实例,因此我们将不使用虚拟继承。

事实上,发生这种情况是因为在第二种情况下,我们尝试使用继承,其中正确的模式应该是组合:图形实际上包含一个点和一个圆。这种非虚继承导致了图形的位置是什么?等复杂的问题。出于这个原因,OO 建议使用 composition over inheritance 规则。

但正如在上面的链接页面中更详细地讨论的那样,继承具有 技术 优点:派生 class 可以立即访问其基础 classes 方法,而在组合模式中,您必须明确地实现它们,即使是构建一个简单的转发实现。在某些情况下,它会导致大量的沸腾代码,代码越多,测试成本越高(或者如果不这样做,出错的风险就越大...)

TL/DR:多重继承本身就是一个复杂的模式,因此在某些 post C++ 语言(例如 Java 中是不允许的。非虚拟多重继承通常是一种模式气味的暗示,因为在应该使用组合的地方使用了继承。但是如果你知道你为什么使用它(避免代码重复 一个可以接受的原因)并且你记录它,它就可以使用。毕竟是C++语言允许的...

在 C++ 中,继承并不总是意味着子类型化,至少是可访问的子类型化:继承关系具有访问控制,就像成员访问一样。

非虚继承是一种排他性的继承关系。虚拟继承是一种可以共享的继承关系。

非虚拟继承是对象与其直接基 classes 之间的严格关系,就像成员资格是对象与其成员之间的严格关系。虚拟继承是一种松散的关系;在某种程度上,虚拟继承从来都不是间接的。每个构造函数初始化直接基类和成员,并将信息递归地传递给基类子对象的构造函数和最内层成员;但是最派生对象的构造函数直接初始化虚基classes.

继承允许派生 class 覆盖虚函数。因为虚继承是一种共享关系,重写基class虚函数的责任由几个"sibling"classes共享;这些兄弟姐妹必须在如何覆盖(共享)基class.

的(共享)虚函数方面进行合作

你应该在任何时候使用非虚拟继承:

  • 初始化必须直接由直接子class完成,带有特定参数
  • 被 "sibling" class 覆盖是不合适的