如何使用多重继承实现多态行为

How do you implement polymorphic behaviour with multiple inheritance?

本文关键字:多态 实现 何使用 多重继承      更新时间:2023-10-16

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

 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();

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

所以我的问题是解决这个问题的办法是什么?C++是否提供了一种可以解决这些问题的机制,或者指针类型必须在基类之间来回转换?当然这是相当麻烦的,因为我不能直接做下面的

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

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

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

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() { /* ... */ }
};

现在,如果我们创建了一个wooden_door对象,我们可以将其传递给一个期望与door对象一起工作的函数(对door对象的引用或指针),或者一个希望与wood对象一起工作(同样是对CCD_4对象的指针或引用)的函数。

的确,多重继承不会让使用door的函数突然拥有使用wood的任何新功能(反之亦然),但我们并不期望如此。我们所期望的是能够将我们的木门视为一扇可以打开和关闭的门,或者视为一块可以燃烧或弯曲的木头——这正是我们所得到的。

在这种情况下,类AC是接口,E实现两个接口。(通常,您不会有中间类A*0和D。)有几种方法可以解决这个问题。

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

class AandC : public A, public C {};

并由此导出CCD_ 14。然后,您通常会通过AandC*,将其无差别地传递给采用A*或CCD_ 18。需要同一对象中的两个接口的函数将处理用CCD_ 19。

如果接口AC在某种程度上是相关的,比如C提供某些A(但不是全部)可能想要的额外设施则A具有getB()功能可能是有意义的,返回C*(如果对象不支持,则返回空指针CCD_ 27接口)。

最后,如果您有多个接口的混合,那么最干净的解决方案是维护两个独立的层次结构,一个用于接口,以及另一个与实现部分:

//   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!
};

(我很想说,从设计的角度来看接口应该始终是虚拟的,以允许未来,即使现在不需要。然而,在实践中无法提前预测这种用途的情况相当罕见。)

如果你想了解更多关于这类事情的信息,你可能想阅读巴顿和纳克曼。这本书现在已经过时了(它描述C++98之前),但大多数信息仍然有效。

这应该能在中工作

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

C不能转换为A,因为它不是A;则它只能向下投射到CCD_ 31或CCD_。

使用A*,您可以生成E*,通过它,您总是可以显式地说C::foo()之类的话,但是的,A无法隐式调用C中可能有重写或没有重写的函数。

在这种奇怪的情况下,模板通常是一个很好的解决方案,因为它们可以允许类表现为具有公共继承,即使它们没有。例如,您可以编写一个模板,该模板可以与任何可以调用foo2()的模板一起使用