4

我从未使用过多重继承,但在最近阅读它时,我开始思考如何在我的代码中实际使用它。当我通常使用多态性时,我通常通过创建声明为基类指针的新派生实例来使用它,例如

 BaseClass* pObject = new DerivedClass();

以便在派生类上调用虚函数时获得正确的多态行为。通过这种方式,我可以拥有不同多态类型的集合,这些多态类型通过它们的虚函数管理自己的行为。

在考虑使用多重继承时,我正在考虑相同的方法,但是如果我有以下层次结构,我将如何做到这一点

 class A {
     virtual void foo() = 0;
 };
 class B : public A {
     virtual void foo() {
         // implementation
     }
 };

 class C {
     virtual void foo2() = 0;
 };
 class D : public C {
     virtual void foo2() {
         // implementation
     }
 };

 class E : public C, public B {
     virtual void foo() {
         // implementation
     }
     virtual void foo2() {
         // implementation
     }
 };

有了这个层次结构,我可以创建一个 E 类的新实例

 A* myObject = new E();

或者

 C* myObject = new E();

或者

 E* myObject = new E();

但是如果我将它声明为 aA*那么我将失去类 C 和 D 继承层次结构的多态性。同样,如果我将其声明为,C*那么我将失去 A 类和 B 类多态性。如果我声明它,E*那么我无法以我通常的方式获得多态行为,因为对象不是通过基类指针访问的。

所以我的问题是解决这个问题的方法是什么?C++ 是否提供了一种可以解决这些问题的机制,还是必须在基类之间来回转换指针类型?当然这很麻烦,因为我无法直接执行以下操作

 A* myA = new E();
 C* myC = dynamic_cast<C*>(myA);

因为强制转换会返回一个 NULL 指针。

4

4 回答 4

7

使用多重继承,您可以使用多种不同的方式查看单个对象。例如,考虑:

class door { 
   virtual void open();
   virtual void close();
};

class wood { 
    virtual void burn();
    virtual void warp();
};

class wooden_door : public wood, public door {
    void open() { /* ... */ }
    void close() { /* ... */ }
    void burn() { /* ... */ }
    void warp() { /* ... */ }
};

现在,如果我们创建了一个 wood_door 对象,我们可以将它传递给一个期望与门对象(引用或指针)一起工作的函数,或者期望与门对象(再次,指针或引用)一起工作的函数wood目的。

确实,多重继承不会突然为与 s 一起工作的函数提供door任何新的功能wood(反之亦然)——但我们并不真正期望这一点。我们期望的是能够将我们的木门视为一扇不能打开和关闭的门,或者是一块可以燃烧或弯曲的木头——这正是我们得到的。

于 2012-08-02T07:18:04.103 回答
3

在这种情况下,类AC是接口,并E实现了两个接口。(通常情况下,您不会有中间类CD在这种情况下。)有几种方法可以处理这个问题。

最常见的可能是定义一个新接口,它是 和 的A总和C

class AandC : public A, public C {};

E由此衍生。然后,您通常会E通过 a 进行管理AandC*,将其无差别地传递给采用 aA*或 a 的 函数C*。在同一个对象中需要两个接口的函数将处理AandC*.

如果接口AC以某种方式相关,比如说C提供了一些(但不是全部)可能想要支持的额外设施,那么拥有一个返回(或空指针,如果对象不支持)的函数A可能是有意义的支持接口)。AgetB()C*C

最后,如果你有 mixins多个接口,最简洁的解决方案是维护两个独立的层次结构,一个用于接口,另一个用于实现部分:

//   Interface...
class AandC : public virtual A, public virtual C {};

class B : public virtual A
{
    //  implement A...
};

class D : public virtual C
{
    //  implement C...
};

class E : public AandC, private B, private D
{
    //  may not need any additional implementation!
};

(我想说的是,从设计的角度来看,接口的继承应该始终是虚拟的,以便将来允许这种事情,即使现在不需要。然而,在实践中,这似乎是公平的很少能够提前预测这种用途。)

如果您想了解有关此类事情的更多信息,您可能需要阅读 Barton 和 Nackman。这本书现在已经过时了(它描述了 C++98 之前的内容),但大部分信息仍然有效。

于 2012-08-02T08:07:03.680 回答
1

这应该工作

 A* myA = new E();
 E* myC = dynamic_cast<E*>(myA);
 myC->Foo2();
于 2012-08-02T07:15:46.373 回答
0

C不能转换为,A因为它不是A; 它只能向下转换为Dor E

使用A*你可以做一个E*并且通过它你总是可以明确地说C::foo()但是是的,没有办法A隐式调用C可能有覆盖或可能没有覆盖的函数。

在这种奇怪的情况下,模板通常是一个很好的解决方案,因为它们可以让类表现得好像它们具有公共继承一样,即使它们没有。例如,您可能会编写一个模板,该模板适用于可以foo2()在其上调用的任何内容。

于 2012-08-02T07:16:11.600 回答