我需要一个何时使用非虚拟多重继承的简单示例
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 覆盖是不合适的
我教 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 覆盖是不合适的