用于多重继承?


15

任何人都可以想到任何情况下使用多重继承?我能想到的每一种情况下可以通过该方法操作来解决

AnotherClass() { return this->something.anotherClass; } 
  0

这是回答您的问题吗?如果是这样,请接受答案。 (提示:+12答案可能是答案) 03 1月. 102010-01-03 12:31:46

21

大多数全尺寸的多重继承的用途是混入。举个例子:

class DraggableWindow : Window, Draggable { } 
class SkinnableWindow : Window, Skinnable { } 
class DraggableSkinnableWindow : Window, Draggable, Skinnable { } 

等等

在大多数情况下,最好使用多重继承做严格的接口继承。

class DraggableWindow : Window, IDraggable { } 

然后在DraggableWindow类中实现IDraggable接口。编写好的mixin类太难了。

MI方法的好处(即使您只使用Interface MI),您可以将各种不同的Windows视为Window对象,但可以灵活地创建不可能的事物(或更多困难)与单一继承。

例如,在许多类的框架,你看到这样的内容:

class Control { } 
class Window : Control { } 
class Textbox : Control { } 

现在,假设你想和窗口特征的文本框?像被dragable,有一个标题栏,等等......你可以做这样的事情:

class WindowedTextbox : Control, IWindow, ITexbox { } 

在单继承模型,你不能轻易地从两个窗口和文本框继承,而无需一些问题,重复控制对象和其他类型的问题。您还可以将WindowedTextbox视为窗口,文本框或控件。

此外,为了解决.anotherClass()成语,.anotherClass()返回一个不同的对象,而多重继承允许同一个对象用于不同的目的。


12

我觉得多重继承特别有用使用mixin类时。

如维基百科指出:

在面向对象的编程 语言,一个mixin是一类 提供一定的功能,以便 由子类继承,而不是 意味着独立。

我们的产品如何使用mixin类的一个例子是用于配置保存和恢复目的。有一个抽象混合类定义了一组纯虚拟方法。任何可保存的类都会从保存/恢复混入类继承,这会自动为其提供适当的保存/恢复功能。

但是它们也可能作为其普通类结构的一部分从其他类继承,所以这些类在这方面使用多重继承是很常见的。

多重继承的例子:

class Animal 
{ 
    virtual void KeepCool() const = 0; 
} 

class Vertebrate 
{ 
    virtual void BendSpine() { }; 
} 


class Dog : public Animal, public Vertebrate 
{ 
    void KeepCool() { Pant(); } 
} 

做任何形式的公有继承(一个或多个)的时候什么是最重要的是要尊重关系。如果“类”是这些对象中的一个,则该类只应从一个或多个类继承。如果它只是“包含”其中一个对象,则应该使用聚合或组合。

上面的例子结构合理,因为狗是动物,也是脊椎动物。

  0

这不是多重继承。它是接口的使用,但是C++恰好以相同的方式实现它。 21 2月. 092009-02-21 23:57:59

  0

@Erik - 如果mixin类是完全抽象的,它只是一个接口。在我给出的代码示例中,Vertebrate :: BendSpine()有一个实现,尽管它是空的。即使它不是最好的例子,我仍然会称这种多重继承。 22 2月. 092009-02-22 00:01:18

+1

脊椎动物是动物的一个亚类,而Dog只是将脊椎动物划分子类,是不是更有意义?最后我检查过,所有的脊椎动物都是动物。 22 2月. 092009-02-22 00:18:18

+2

像这样的更好的例子是FlyingCreature作为mixin。大多数鸟类和一些哺乳动物(蝙蝠)会继承FlyingCreature以及Bird或Mammal基类。对于其他属性也是如此,这影响了通常分类的行为。 22 2月. 092009-02-22 00:28:53

  0

不是最好的例子,但重点在于...而不是具有*深*继承结构,这在许多情况下是过度杀伤的,您可以从所需的父属性继承(或混入)并获得相同的结果。 22 2月. 092009-02-22 00:29:19

  0

@Kibbee - 这是一个很好的观点。也许这不是班级层次结构的最好例子,但是在我编辑答案时,这是我能想到的最好的例子。希望我的关于多继承如何支持mixin的观点仍然清晰。 23 2月. 092009-02-23 10:09:37


2

我最近参与过的一个案例涉及到启用网络的标签打印机。我们需要打印标签,所以我们有一个LabelPrinter类。该课程具有打印多个不同标签的虚拟调用。我也有一个TCP/IP连接的东西的通用类,它可以连接,发送和接收。 因此,当我需要实现打印机时,它从LabelPrinter类和TcpIpConnector类继承。


2

我认为fmsf的例子是一个坏主意。汽车不是轮胎或发动机。你应该使用组合。

MI(实现或接口)可用于添加功能。这些通常称为混合类。想象一下你有一个GUI。有处理绘图的视图类和一个拖动处理拖放的类&。如果你有一个对象,它都你会喜欢

class DropTarget{ 
public void Drop(DropItem & itemBeingDropped); 
... 
} 

class View{ 
    public void Draw(); 
... 
} 

/* View you can drop items on */ 
class DropView:View,DropTarget{ 

} 

0

下面的例子一类主要是一些我在C++中经常看到:有时可能是必要的,因为你需要的实用工具类,而是因为他们的设计不能通过组合来使用(至少不是有效的,或者没有使代码更加混乱而不是退回到多继承)。一个很好的例子是你有一个抽象基类A和一个派生类B,而B也需要是一种可序列化的类,所以它必须派生自另一个叫做Serializable的抽象类。这是可能的,以避免MI,但如果序列化只包含几个虚拟方法,需要B的私有成员深访问,那么它可能是值得弄脏继承树只是为了避免和我交朋友的声明和放弃获得B的内部的一些帮手组成班。


4

大多数人使用多重继承申请多个接口一类的上下文。这是Java和C#等方法的执行方式。

C++允许你相当自由地应用多个基类,在一个is-a的类型之间的关系。所以,你可以像派生它的任何基类一样对待派生对象。

另一个用途,如LeopardSkinPillBoxHat points out,是混合插件。一个很好的例子是来自Andrei Alexandrescu的书Modern C++ Design的Loki library。他使用他所称的策略类,它们通过继承来指定给定类的行为或要求。

另一种用途是简化模块化方法,该方法允许API-independence通过在经典钻石层级中使用姊妹级代表团。

mi的用途有很多。滥用的可能性更大。


4

Java有接口。 C++没有。

因此,可以使用多继承来模拟的接口功能。 如果您是C#和Java程序员,每次您使用扩展基类但也实现了一些接口的类时,您都可以在某些情况下承认多继承是有用的。

  0

我同意这一点:)关于学习C++和稍后C#的最糟糕的事情是mixin的约束。我有时会发现自己复制粘贴实现了一个mixin接口,这可能是我的部分代码或设计不佳,但我不能想到其他任何事情 - 作为一个程序员,你讨厌复制粘贴 23 2月. 092009-02-23 20:35:19

  0

C++模拟接口100%与抽象类。你在谈论什么“界面功能”?接口的多重继承的Java/C#版本? 30 3月. 092009-03-30 19:17:23

  0

C++没有'interface'关键字,因为它不需要它(它有多重继承)。如果其他语言(如Java和C#)具有接口,则C++中的多重继承必须具有一些用例。 31 3月. 092009-03-31 17:15:35


2

,我认为这将是一个样板代码最有用的。例如,.NET中所有类的IDisposable模式都是完全相同的。那么为什么重复输入代码呢?

又如ICollection的。绝大多数的接口方法都是完全相同的。只有几种方法对你的课程实际上是独一无二的。

不幸的是多重继承是很容易被滥用。人们将很快开始从他们的TcpIpConnector类做这样LabelPrinter类继承愚蠢的事情,而不是仅仅包含它。

  0

理论上你是对的,但在实践中很难得到正确的答案。价格(虚拟继承构造函数)非常高。 22 2月. 092009-02-22 00:24:35

  0

只有在需要通过多个继承分支继承公共基础时才支付该价格。在实践中,这种情况很少发生(如果重复的基地没有任何状态,那么这个问题就变成了非问题) 22 2月. 092009-02-22 00:30:44

+1

“人们将很快开始做像傻瓜一样的事情,像LabelPrinter类继承自它们的TcpIpConnector类,而不是仅仅包含它。”真?我已经听到了很多,但从来没有任何证据表明它实际上正在发生。我读过的所有材料都对组合和继承进行了非常明确的区分,我不明白任何人如何能让这两者混淆。 22 1月. 132013-01-22 10:56:39


1

确实,接口(Java或C#like)的组合以及转发给助手可以模拟多重继承(特别是mixin)的许多常见用法。然而,这是以重复转发代码为代价(并且违反了DRY)。

MI确实打开了许多困难的领域,最近一些语言设计者已经决定MI的潜在缺陷大于好处。类似地,人们可以针对泛型进行争论(异构容器可以工作,循环可以用(尾)递归来替代)以及几乎任何其他编程语言的特性。仅仅因为可以在没有功能的情况下工作并不意味着该功能毫无价值,或者无法帮助有效地表达解决方案。

丰富多样的语言和语言系列让我们更容易作为开发人员选择解决当前业务问题的好工具。我的工具箱包含很多我很少使用的项目,但在那些场合我不想把所有东西当作钉子来对待。

+1

如果语言功能可能非常有用,但通常会被严重滥用,它可能会以C++存在,并且在Java中不存在。不同的语言设计哲学。 23 2月. 092009-02-23 20:58:24

+1

是的。 Java是为白痴设计的。 22 1月. 132013-01-22 10:58:54


1

的我们的产品如何使用混入类的一个例子是配置保存和恢复的目的。有一个抽象混合类定义了一组纯虚拟方法。任何可保存的类都会从保存/恢复混入类继承,这会自动为其提供适当的保存/恢复功能。

这个例子并没有真正说明多继承的有用性。这里定义的是INTERFACE。多继承允许您继承行为。 mixin的要点是什么?

一个例子;由于需要保持向后兼容性,我必须实现自己的序列化方法。

所以每一个对象获得一个读取和存储方法是这样的。

Public Sub Store(ByVal File As IBinaryWriter) 
Public Sub Read(ByVal File As IBinaryReader) 

我也希望能够分配和克隆对象。所以我想在每个对象上都这样。

Public Sub Assign(ByVal tObject As <Class_Name>) 
Public Function Clone() As <Class_Name> 

现在在VB6中我重复了这段代码。

Public Assign(ByVal tObject As ObjectClass) 
    Me.State = tObject.State 
End Sub 

Public Function Clone() As ObjectClass 
    Dim O As ObjectClass 
    Set O = New ObjectClass 
    O.State = Me.State 
    Set Clone = 0 
End Function 

Public Property Get State() As Variant 
    StateManager.Clear 
    Me.Store StateManager 
    State = StateManager.Data 
End Property 

Public Property Let State(ByVal RHS As Variant) 
    StateManager.Data = RHS 
    Me.Read StateManager 
End Property 

请注意,Statemanager是一个读取和存储字节数组的流。

此代码重复数十次。

现在在.NET中,我可以通过使用泛型和继承的组合来解决此问题。我在.NET版本下的对象在从MyAppBaseObject继承时获取Assign,Clone和State。但我不喜欢每个对象都从MyAppBaseObject继承的事实。

我宁愿只是在Assign Clone界面和BEHAVIOR中混合使用。更好的是,分开混合Read和Store接口,然后可以混合分配和克隆。在我看来,这将是更干净的代码。

但我重用行为的时间在我使用Interface的时候是DWARFED。这是因为大多数对象层次结构的目标不是重复使用行为,而是精确定义不同对象之间的关系。哪些接口是专门设计的。所以尽管C#(或VB.NET)有一定的能力可以做到这一点,但在我看来,它并不是一个表现障碍。

整个原因,这甚至是一个问题,即C++首先在接口与继承问题中摸索球。当OOP首次亮相时,每个人都认为行为重用是首要任务。但是,这被证明是一个嵌合体,只对特定情况有用,比如制作一个UI框架。

后来开发了mixins(以及面向方面编程中的其他相关概念)的概念。发现多重继承有助于创建混合。但是C#是在这个被广泛认可之前开发的。可能会开发一种替代语法来执行此操作。


1

我怀疑在C++中,MI最适合作为框架的一部分(之前讨论过的混合类)。我唯一知道的就是每当我尝试在我的应用程序中使用它时,我最后都会后悔选择,并且经常撕毁并用生成的代码替换它。

MI是其中一种'如果您真的需要它,请使用它,但请确保您真的需要它'工具。


0

我不得不使用它的今天,居然......

这里是我的情况 - 我有一个地方一个包含零个或多个烧烤(数组中的代表)在内存中代表的域模型中,每个B有零个或多个Cs,以及Cs到Ds。我无法改变他们是数组的事实(这些数组的来源是来自构建过程中自动生成的代码)。每个实例都需要跟踪它们所属的父数组中的哪个索引。他们还需要跟踪其父代的实例(关于原因的详细信息)。我写了这样的事情(有更多的是它,这在语法上不正确的,它仅仅是一个例子):

class Parent 
{ 
    add(Child c) 
    { 
     children.add(c); 
     c.index = children.Count-1; 
     c.parent = this; 
    } 
    Collection<Child> children 
} 

class Child 
{ 
    Parent p; 
    int index; 
} 

然后,对于域类型,我这样做:

class A : Parent 
class B : Parent, Child 
class C : Parent, Child 
class D : Child 

实际的实现是在C#中使用接口和泛型,并且我不能像我的语言支持它那样进行多重继承(必须完成一些复制粘贴)。所以,我认为我会搜索以查看人们对多重继承的看法,并且我得到了您的问题;)

我无法使用.anotherClass的解决方案,因为为Parent实施了add(引用这个 - 我想这不是其他类)。

它变得更糟,因为生成的代码有一个子类别的东西,既不是父母或孩子......更多的复制粘贴。

+1

看来你正在使这个更复杂,而不是必要的。为什么不只是有一个“节点”,它有一个父节点(可以是NULL)和多个子节点(可以是空的)。然后简单地让A,B,C和D都从Node继承。 30 4月. 092009-04-30 01:06:28

  0

我希望能够说你不能引用A的父亲或添加到孩子D(因为A没有父母,D没有孩子)。在我的实现中,如果你尝试,它将不会编译。我也用泛型改进了这个,所以你不能将C添加到A,C或D,或者如果你引用B的父对象,你不需要将它转换为A(它将是A已经通过泛型)等。 15 5月. 092009-05-15 00:56:06