如何正确实现多重继承?

时间:2014-05-23 19:05:46

标签: c++ inheritance c++11 multiple-inheritance

我花了很多时间搜索有关此主题的信息,但我只能在碎片中找到它,因为大量的警告不使用多重继承而感到阴郁。

我对多重继承有多糟糕感兴趣。我对有效的用例也不感兴趣。我得到的信息是,你应该尽可能地避免它,并且几乎总有更好的选择。

但我想知道的是,在 你决定使用多重继承的时候,你是如何正确地做到的?

副主题我想更详细地解释一下:

  • 多态性的精确机制
    • 混合虚拟和纯虚拟基类
    • 重复功能
  • 内存管理
  • 在多个层面解决钻石问题
  • 混合公共和私人继承
  • 混合虚拟和非虚拟继承

并且,如果适用:

  • C ++和C ++之间的差异11

1 个答案:

答案 0 :(得分:3)

采用以下层次结构:

  • 基类A
  • BCE继承自A
  • D继承了BC
  • F继承自DE

在代码中说:

class A { public: int a; }
class B : public A { }
class C : public A { }
class D : public B, public C { }
class E : public A { }
class F : public D, public E { }

或图表:

       A     A   A
       |     |   |
       |     |   |
       B     C   E
        \   /   /
         \ /   /
          D   /
           \ /
            F

通过这种结构,每个B,C和E都拥有自己的A副本。接下来,D持有B和C的副本,F持有D和E的副本。

这会导致问题:

D d;
d.a = 10; // ERROR! B::a or C::a?

对于这种情况,您使用虚拟继承,创建一个"钻石":

          A      A
         / \     |
        /   \    |
       B     C   E
        \   /   /
         \ /   /
          D   /
           \ /
            F

或代码:

class A { public: int a; }
class B : public virtual A { }
class C : public virtual A { }
class D : public B, public C { }
class E : public A { }
class F : public D, public E { }

现在你解决了上一个问题,因为B::aC::a共享相同的内存,但同样的问题仍然存在,在另一个层面:

F f;
f.a = 10; // ERROR: D::a or E::a ?

这部分我不确定 Confirmed:您可以使用A for E的virtual继承来解决此问题。但我会保持原样,以回答另一点:混合虚拟和非虚拟继承。

但请注意,您希望来自E::a的{​​{1}}具有来自同一F的不同D::a值。为此,您必须输入您的F

F

现在,您的F *f = new F; (static_cast<D*>(f))->a = 10; (static_cast<E*>(f))->a = 20; 拥有两个不同的F* f值。

关于内存管理

从以上示例中学习这些课程:

A::a

可以绘制以下内存图:

对于A类:

class A { public: int a; }
class B : public virtual A { }
class C : public virtual A { }
class D : public B, public C { }
class E : public A { }
class F : public D, public E { }

对于B,C和E类:

+---------+
|    A    |
+---------+

意味着对于您创建的B,C和E的每个实例,您将创建另一个A实例。

对于D类,事情有点复杂:

+---------+
|    B    |
+---------+
     |
     V
+---------+
|    A    |
+---------+

这意味着当您创建D时,您有一个B实例和一个C实例。但是不是为每个B和C创建一个新的A实例,而是为这两个实例创建一个实例。

对于F:

+---------------------------------------+
|                   D                   |
+---------------------------------------+
       |                         |
       V                         V
+--------------+         +--------------+
|      B       |         |       C      |
+--------------+         +--------------|
       |                         |
       V                         V
+---------------------------------------+
|                   A                   |
+---------------------------------------+

这意味着当你创建一个F时,你有:D的一个实例和E的一个实例。由于E实际上并不从A继承,所以在创建E时会创建一个新的A实例。

关于虚拟和纯虚拟方法

参加以下课程:

+-------------------------------------------------------+
|                           F                           |
+-------------------------------------------------------+
                    |                              |
                    V                              V
+---------------------------------------+     +---------+
|                   D                   |     |    E    |
+---------------------------------------+     +---------+
       |                         |                 |
       V                         V                 |
+--------------+         +--------------+          |   
|      B       |         |       C      |          |
+--------------+         +--------------+          |
       |                         |                 |
       V                         V                 V
+---------------------------------------+     +---------+
|                   A                   |     |    A    |
+---------------------------------------+     +---------+

class A { virtual void f() = 0; } class B : public A { virtual void f(int value) { std::cout << "bar" << value; } } class C : public B { virtual void f() { std::cout << "foo"; f(42); } } 被称为A(有些也称为abstract),因为有纯虚函数。

interface也是B,因为它继承自abstract并且不会覆盖A方法,这是纯虚拟的,甚至可以定义自己的方法({{ 1}})

A::f(void)B::f(int)的{​​{1}},因为它确定了将C转换为&#34;完整&#34}所需的所有功能。 class - 覆盖implementation

这个答案并不完整,但它给出了一个大致的想法。