Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 多重继承的确切问题是什么?_Oop_Multiple Inheritance_Language Theory - Fatal编程技术网

Oop 多重继承的确切问题是什么?

Oop 多重继承的确切问题是什么?,oop,multiple-inheritance,language-theory,Oop,Multiple Inheritance,Language Theory,我可以看到人们一直在问,下一个版本的C#或Java是否应该包含多重继承。有幸拥有这种能力的C++人说,这就像是给某人一根绳子,最后挂上自己。 多重继承有什么问题?有混凝土样品吗 假设对象A和B都是由C继承的。A和B都实现了foo(),而C没有。我称之为C.foo()。选择哪个实现?还有其他问题,但这类问题很严重。tloach的例子很好地总结了多重继承的主要问题。当从实现同一函数或字段的多个基类继承时,编译器必须决定继承哪个实现 当您从多个继承自同一基类的类继承时,情况会更糟。(菱形继承,如果绘制

我可以看到人们一直在问,下一个版本的C#或Java是否应该包含多重继承。有幸拥有这种能力的C++人说,这就像是给某人一根绳子,最后挂上自己。
多重继承有什么问题?有混凝土样品吗

假设对象A和B都是由C继承的。A和B都实现了foo(),而C没有。我称之为C.foo()。选择哪个实现?还有其他问题,但这类问题很严重。

tloach的例子很好地总结了多重继承的主要问题。当从实现同一函数或字段的多个基类继承时,编译器必须决定继承哪个实现

当您从多个继承自同一基类的类继承时,情况会更糟。(菱形继承,如果绘制继承树,将得到菱形形状)

对于编译器来说,这些问题并不是真正需要克服的问题。但是编译器在这里必须做出的选择是相当随意的,这使得代码更加不直观

我发现,在进行良好的OO设计时,我从不需要多重继承。在我确实需要它的情况下,我通常会发现我一直在使用继承来重用功能,而继承只适用于“is-a”关系

还有其他一些技术,如mixin,可以解决相同的问题,而不存在多重继承的问题。

:

当两个类B和C继承自A,而类D继承自B和C时产生的歧义。如果A中有一个方法B和C都有,而D没有覆盖它,那么D继承的是该方法的哪个版本:B的版本,还是C的版本

…它被称为“钻石问题”,因为在这种情况下类继承图的形状。在这种情况下,A类位于顶部,B和C分别位于其下方,D在底部将两者连接在一起,形成菱形


多重继承是不经常使用的东西之一,可能被误用,但有时是需要的


我从不理解不添加一个特性,仅仅因为它可能被误用,当没有好的替代品时。接口不是多重继承的替代品。首先,他们不允许你强制执行先决条件或后决条件。与任何其他工具一样,您需要知道何时适合使用它,以及如何使用它。

最明显的问题是函数重写

假设有两个类
A
B
,它们都定义了一个方法
doSomething
。现在,您定义了第三个类
C
,该类继承自
a
B
,但不重写
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
?或者它实际上是通过其
B
接口以多态方式调用的
C
对象?根据对象的实际标识,物理布局将有所不同,并且不可能知道要在调用站点调用的函数的偏移量

处理此类系统的方法是抛弃固定布局方法,允许在尝试调用函数或访问其字段之前查询每个对象的布局


所以…长话短说…对于编译器作者来说,支持多重继承是件痛苦的事。因此,当像Guido van Rossum这样的人设计python,或者当Anders Hejlsberg设计c#时,他们知道支持多重继承将使编译器实现变得更加复杂,他们可能认为这样的好处不值得付出代价。

你们提到的问题其实并不难解决。事实上,埃菲尔做得非常好!(并且不引入任意选择或其他内容)

例如,如果您从A和B继承,两者都有方法foo(),那么您当然不希望在类C中任意选择从A和B继承。 您必须重新定义foo,以便清楚在调用c.foo()时将使用什么,或者必须重命名c中的一个方法(它可能成为bar())


我还认为多重继承通常非常有用。如果你看看Eiffel的库,你会发现它到处都在使用,就我个人而言,当我不得不回到Java编程时,我错过了这个特性。

多重继承本身没有什么错。问题是要将多重继承添加到一种从一开始就没有考虑多重继承的语言中

埃菲尔语言以一种非常高效和高效的方式支持无限制的多重继承,但该语言从一开始就被设计为支持多重继承

对于编译器开发人员来说,这个特性实现起来很复杂,但是这个缺点似乎可以通过一个goo来弥补
public sealed class CustomerEditView : Form, MVCView<Customer>
class X { public virtual void Foo() { Console.WriteLine("XFoo"); }
class Y : X {};
class Z : X {};
class W : Y, Z  // Not actually permitted in C#
{
  public static void Test()
  {
    var it = new W();
    it.Foo();
  }
}
class Y : X { public override void Foo() { Console.WriteLine("YFoo"); }
class Z : X { public override void Foo() { Console.WriteLine("ZFoo"); }
  A       A
 / \     / \
B   C   D   E
 \ /     \ /
  F       G
    \   /
      H
  A
 / \
B   B
|   |
C   D
 \ /
  E
struct A { virtual ~A() {} /* so that the class is polymorphic */ };
struct B: virtual A {};
struct C: B {};
struct D: B {};
struct E: C, D {};

int main() {
        E data;
        E *e = &data;
        A *a = dynamic_cast<A *>(e); // works, A is unambiguous
//      B *b = dynamic_cast<B *>(e); // doesn't compile
        B *b = dynamic_cast<B *>(a); // NULL: B is ambiguous
        std::cout << "E: " << e << std::endl;
        std::cout << "A: " << a << std::endl;
        std::cout << "B: " << b << std::endl;
// the next casts work
        std::cout << "A::C::B: " << dynamic_cast<B *>(dynamic_cast<C *>(e)) << std::endl;
        std::cout << "A::D::B: " << dynamic_cast<B *>(dynamic_cast<D *>(e)) << std::endl;
        std::cout << "A=>C=>B: " << dynamic_cast<B *>(dynamic_cast<C *>(a)) << std::endl;
        std::cout << "A=>D=>B: " << dynamic_cast<B *>(dynamic_cast<D *>(a)) << std::endl;
        return 0;
}