我可以看到人们一直在询问是否应该在下一版本的C#或Java中包含多重继承.有幸拥有这种能力的C++人说,这就像给某人一条绳子最终自我吊死.
多重继承有什么问题?有没有具体的样品?
最明显的问题是功能覆盖.
假设有两个类A和B,它们都定义了一个方法"doSomething".现在定义一个继承自A和B的第三个类C,但是不要覆盖"doSomething"方法.
当编译器播种此代码时......
C c = new C(); c.doSomething();
...应该使用哪种方法实现?在没有任何进一步澄清的情况下,编译器无法解决模糊性问题.
除了重写之外,多重继承的另一个大问题是内存中物理对象的布局.
像C++,Java和C#这样的语言为每种类型的对象创建一个固定的基于地址的布局.像这样的东西:
class A: at offset 0 ... "abc" ... 4 byte int field at offset 4 ... "xyz" ... 8 byte double field at offset 12 ... "speak" ... 4 byte function pointer class B: at offset 0 ... "foo" ... 2 byte short field at offset 2 ... 2 bytes of alignment padding at offset 4 ... "bar" ... 4 byte array pointer at offset 8 ... "baz" ... 4 byte function pointer
当编译器生成机器代码(或字节码)时,它使用这些数字偏移来访问每个方法或字段.
多重继承使它非常棘手.
如果C类继承自A和B,则编译器必须决定是以AB顺序还是以BA顺序布局数据.
但现在想象一下你在B对象上调用方法.它真的只是一个B吗?或者它实际上是一个C对象通过其B接口多态调用?根据对象的实际身份,物理布局将不同,并且无法知道要在呼叫站点调用的函数的偏移量.
处理这种系统的方法是抛弃固定布局方法,允许在尝试调用函数或访问其字段之前查询每个对象的布局.
所以......长话短说......编译器作者支持多重继承是一件痛苦的事.因此,当像Guido van Rossum这样的人设计python时,或者当Anders Hejlsberg设计c#时,他们知道支持多重继承将使编译器实现变得更加复杂,并且可能他们认为这种好处不值得花费.
你们提到的问题并不是很难解决.事实上,例如埃菲尔完美地做到了!(并且不引入任意选择或其他)
例如,如果你从A和B继承,两者都有方法foo(),那么你当然不希望你的类C继承A和B的任意选择.你必须重新定义foo所以它很清楚会是什么如果调用c.foo()或者你必须重命名C中的一个方法,则使用它(它可能变为bar())
另外我认为多重继承通常非常有用.如果你看一下埃菲尔的图书馆,你会发现它已经遍布整个地方,而且当我不得不回到Java编程时,我已经错过了这个功能.
钻石问题:
当两个类B和C继承自A,而D类继承自B和C时出现歧义.如果A中的方法B和C已重写,而D不覆盖它,那么哪个版本的D继承的方法:B的那个,还是C的?
...由于这种情况下类继承图的形状,它被称为"钻石问题".在这种情况下,A级位于顶部,B和C分别位于其下方,D将两者连接在一起形成菱形......
多重继承是经常不使用的东西之一,可能被滥用,但有时需要.
我从来没有理解不添加功能,只是因为它可能被滥用,没有好的选择.接口不是多重继承的替代方案.首先,它们不允许您强制执行先决条件或后置条件.就像任何其他工具一样,您需要知道何时使用它以及如何使用它.
假设你有对象A和B都由C继承.A和B都实现了foo()而C没有.我叫C.foo().选择哪种实施方案?还有其他问题,但这类事情很重要.
多重继承的主要问题很好地总结了tloach的例子.当从实现相同函数或字段的多个基类继承时,编译器必须决定要继承哪个实现.
当从多个继承自同一基类的类继承时,这会变得更糟.(钻石继承,如果你绘制继承树,你得到钻石形状)
对于编译器来说,这些问题并不是真正的问题.但是编译器必须在这里做出的选择是相当随意的,这使代码更不直观.
我发现在做好OO设计时我从不需要多重继承.在我需要它的情况下,我通常发现我一直在使用继承来重用功能,而继承只适用于"is-a"关系.
还有其他技术,如mixin,可以解决相同的问题,并且没有多重继承所具有的问题.
我不认为钻石问题是一个问题,我会认为这是诡辩,没有别的.
从我的角度来看,最糟糕的问题是多重继承是RAD - 受害者和声称自己是开发人员的人,但实际上他们仍然处于半知识状态(充其量).
就个人而言,如果我最终可以在Windows窗体中做这样的事情(这不是正确的代码,但它应该给你的想法),我会很高兴:
public sealed class CustomerEditView : Form, MVCView
这是我没有多重继承的主要问题.您可以使用接口执行类似的操作,但是我称之为"s***代码",这是一个令人痛苦的重复性问题,例如,您必须在每个类中编写以获取数据上下文.
在我看来,对于现代语言中的任何重复代码,应该绝对没有必要,也不是最轻微的.