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

时间:2017-01-25 08:40:25

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

我教C ++,我需要一个好的,简单的日常例子,当你使用它时:

SELECT 
    USERINFO.Name, 
    USERINFO.SSN, 
    USERINFO.Badgenumber, 
    FORMAT(CHECKINOUT.CHECKTIME, "AMPM") AS [AM/PM],
    FORMAT((Min(CHECKINOUT.CHECKTIME)),"dd-mmm-yyyy hh:mm am/pm") AS TIMEIN, 
    FORMAT((Max(CHECKINOUT.CHECKTIME)),"dd-mmm-yyyy hh:mm am/pm") AS TIMEOUT
FROM 
    CHECKINOUT 
INNER JOIN 
    USERINFO ON CHECKINOUT.USERID = USERINFO.USERID
WHERE 
    (CHECKINOUT.CHECKTIME >= DATEVALUE(PERIOD_START) AND
     CHECKINOUT.CHECKTIME < DATEADD("d", 1, PERIOD_END) AND
    USERINFO.Name = Insert_Name
GROUP BY 
    CHECKINOUT.USERID, 
    USERINFO.Name, 
    USERINFO.SSN, 
    DateValue(CHECKINOUT.CHECKTIME), 
    FORMAT(CHECKINOUT.CHECKTIME, "AMPM"),
    USERINFO.Badgenumber
HAVING 
    (((USERINFO.Badgenumber) < "8000"))
ORDER BY 
    USERINFO.Name, 
    FORMAT(CHECKINOUT.CHECKTIME, "AMPM"),
    Min(CHECKINOUT.CHECKTIME);

这里D继承了两次,因为没有涉及虚拟继承。

我需要一个易于理解的动机示例,说明何时需要此构造。我甚至不需要代码;我只需要能够向我的学生展示一个例子,他们就会惊呼,#34;是的,我可以看到那将是有用的!&#34;

4 个答案:

答案 0 :(得分:2)

简短版本:钻石配置中的多个非虚拟继承 从不 有用

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

我理解你对你的问题的意思,如果语言中存在某种东西,那肯定是因为某些原因。它至少对于非常狭窄的情况很有用。但我宁愿以这种方式看待情况:

  • C ++中存在多重继承(MI),可用于在一个
  • 中组合多个接口
  • 虚拟MI存在于C ++中,对解决钻石问题非常有用
  • 钻石配置中的非虚拟MI仅作为MI的副作用而存在,并且是不合需要的。

对于虚拟MI,我喜欢使用分离接口和实现层次结构(或扩展接口)的示例。例如,当你有这样的配置时: enter image description here

答案 1 :(得分:1)

我建议这个例子用于虚拟和非虚拟多重继承:

A - &gt;人(身份证,姓名)

B - &gt;老师(薪水)

C - &gt;学生(商标)

D - &gt; AssitantTeacher

对我来说,助理教师是一名刚毕业的老师,正在教授和准备更高学位(硕士或博士)。他有薪水和分数。

要添加非虚拟风味,假设教师具有特定格式的ID,并且学生具有其他格式的ID。 AssistantTeacher有两个ID:一个教师ID(用作HR数据库中的主键)和一个学生ID(用作学术系统数据库中的PK)。

[编辑]

例如,HR ERP系统会给教师一个自动增量ID,学生系统会给学生提供以下格式:YYYYMMSAAAA(其中Y是学生的入学年份,M是月,S是性别:0或1,A是自动增量id部分)。

助理教师的人力资源系统ID为“1332”,学生编号为“20170100004”。

答案 2 :(得分:1)

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

有关虚拟继承的示例,我将保留您的国际象棋示例

                     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

女王继承了Rook和Bishop的方法,但女王只有一个单一的属性,所以我们将在这里使用C ++虚拟继承。

接下来想象我们构建一个由一个圆和一个点组成的复合几何图形。这是一个&#34;继承&#34;图:

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

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

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

但是正如上面链接页面中详细讨论的那样,继承有技术好东西:派生类可以立即访问其基类方法,而在组合模式中,你必须明确地实现他们甚至建立一个简单的转发实现。在某些情况下,它会导致很多沸腾的拼盘代码,代码越多,测试成本就越高(如果不这样做,则会出现更多的错误风险......)

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

答案 3 :(得分:0)

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

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

非虚拟继承是对象与其直接基类之间的严格关系,例如,成员资格是对象与其成员之间的严格关系。虚拟继承是一种松散的关系;在某种程度上,虚拟继承绝不是间接的。每个构造函数初始化直接基础和成员,并且信息以递归方式传递给基础子对象和最内层成员的构造者;但最派生对象的构造函数直接初始化虚拟基类。

继承允许派生类覆盖虚函数。因为虚拟继承是一种共享关系,所以覆盖基类虚函数的责任在几个“兄弟”类之间共享;这些兄弟姐妹必须合作如何覆盖(共享)基类的(共享)虚函数。

您应该在以下情况下使用非虚拟继承:

  • 初始化必须由直接子类直接完成,具有特定参数
  • 超越“兄弟姐妹”课程是不合适的