mixin或特性比纯多重继承更好吗?


54

C ++具有简单的多重继承,许多语言设计都将其视为危险。但是某些语言(例如Ruby和PHP)使用奇怪的语法来完成相同的操作,并将其称为mixin或traits。我多次听说,mixin /特性比普通的多重继承更难滥用。

是什么使它们危险性降低了?是否有不能用mixins / traits来实现,但是可以用C ++风格的多重继承来实现?他们有可能遇到钻石问题吗?

似乎我们在使用多重继承,但只是借口说它们是混合/特性,因此我们可以使用它们。


7
期待有人对此发布一个写得好,周到和彻底的答案。
罗伯特·哈维

7
Ruby和PHP没有引入mixin和特性。Mixins是在Flavors(1980)中引入的,Traits是在Squeak Smalltalk(2001)中引入的。Flavors是最早的具有多重继承的面向对象语言,它使用了mixins。C ++仅在2.0版本中获得了多重继承,该版本于1989年发布,比Flavors落后9年。因此,问题应该是:Flavors具有简单的mixin,但是某些语言(例如C ++)引入了奇怪的语法来完成相同的事情,并将其称为多重继承。
约尔格W¯¯米塔格

Answers:


31

与成熟类一起使用时,多重继承存在许多问题,但它们都围绕模糊性展开

模棱两可的显示方式有几种:

  1. 如果您有两个具有相同字段的基类x,并且派生类型要求x,它将得到什么?
    • 如果两个x变量的类型不一致,则可以推断出来。
    • 如果它们是同一类型,则可以尝试将它们合并到同一变量中。
    • 您总是可以将它们公开为奇怪的完全限定名称。
  2. 如果您有两个具有相同功能f且具有相同签名的基类,并且有人调用f,哪个被调用?
    • 如果两个基类共享另一个共同的虚拟祖先(菱形问题),该怎么办?
    • 如果函数具有不同但兼容的签名怎么办?
  3. 当用两个基类构造一个类时,哪个基类的构造函数首先被调用?当您摧毁物体时,哪个被杀死?
  4. 将对象布置在内存中时,如何始终如一?
  5. 您如何使用3个基类处理所有这些情况?10点

当语言支持全类的多重继承时,这将忽略诸如动态调度,类型推断,模式匹配之类的事情,而我对此一无所知的事情则变得更具挑战性。

特性或混入(或接口,或...)都是专门限制类型功能的构造,因此没有歧义。他们自己很少拥有任何东西。因为没有两个变量或两个函数,所以可以使类型的组合更加平滑。功能和签名。编译器知道该怎么做。

另一种常用的方法是强制用户一次“构建”(或混入)他们的类型。您不必将基类作为新类型的平等伙伴,而是将一种类型添加到另一种类型-覆盖那里的所有内容(通常使用可选语法来重命名和/或重新暴露被覆盖的位)。

是否有不能用mixins / traits来实现,但是可以用C ++风格的多重继承来实现?

根据语言的不同,合并来自多个基类的变量的函数实现和存储,并在派生类型中公开它们通常会变得麻烦或不可能。

他们有可能遇到钻石问题吗?

偶尔会根据您的语言弹出不太严重的变化,但通常不会。特质的全部要点是打破这种歧义。


您能举例说明允许此类“构建”类型的语言/技术吗?
Gherman 2014年

@german-我当然不是Scala专家,但是我的理解是那是该with关键字的作用。
Telastyn 2014年

“专门限制类型功能以免产生歧义的构造”:什么样的限制?接口没有变量,所以这是一个限制,但是Scala特征和Ruby模块却有。他们在其他方面受到限制吗?
David Moles

@DavidMoles-这是常见的方式,我也看到了虚拟调度规则的局限性(想想C#的显式实现接口)。
Telastyn
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.