C++ 如何正确实现多重继承?

C++ 如何正确实现多重继承?,c++,inheritance,c++11,multiple-inheritance,C++,Inheritance,C++11,Multiple Inheritance,我花了很多时间搜索关于这个主题的信息,但我只能找到零碎的信息,因为有大量警告不要使用多重继承 我对多重继承有多糟糕不感兴趣。我对有效的用例也不感兴趣。我得到的信息是,你应该尽可能避免它,而且几乎总是有更好的选择 但我想知道的是,在一个彻底的层面上,当你决定使用多重继承时,你如何正确地做到这一点 我希望更详细地解释的子主题包括: 多态性的精确机制 混合虚拟基类和纯虚拟基类 重复功能 内存管理 多层次解决钻石问题 公私混合继承 混合虚拟继承和非虚拟继承 以及(如适用): C++与C++ 1

我花了很多时间搜索关于这个主题的信息,但我只能找到零碎的信息,因为有大量警告不要使用多重继承

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

但我想知道的是,在一个彻底的层面上,当你决定使用多重继承时,你如何正确地做到这一点

我希望更详细地解释的子主题包括:

  • 多态性的精确机制
    • 混合虚拟基类和纯虚拟基类
    • 重复功能
  • 内存管理
  • 多层次解决钻石问题
  • 公私混合继承
  • 混合虚拟继承和非虚拟继承
以及(如适用):

    C++与C++ 11 的区别
采用以下层次结构:

  • 基类
    A
  • B
    C
    E
    继承自
    A
  • D
    继承自
    B
    C
  • F
    继承自
    D
    E
用密码说:

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?
对于这种情况,您可以使用虚拟继承,创建“菱形”:

或者,在代码中:

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::a
C::a
共享相同的内存,但在另一个级别上仍然存在相同的问题:

F f;
f.a = 10; // ERROR: D::a or E::a ?
这部分我不确定:您也可以使用A for E的
虚拟继承来解决这个问题。但我将保持原样,以回答另一个问题:混合虚拟继承和非虚拟继承

<>但请考虑<代码> E::< <代码> >从<代码> f>代码>中有不同的值<<代码> d::从同一代码< f>代码> < <代码> >。为此,您必须键入您的
F

F *f = new F;
(static_cast<D*>(f))->a = 10;
(static_cast<E*>(f))->a = 20;
可以绘制以下内存图:

A类:

+---------+
|    A    |
+---------+
对于B、C和E类:

+---------+
|    B    |
+---------+
     |
     V
+---------+
|    A    |
+---------+
这意味着,对于您创建的每个B、C和E实例,您都会创建另一个A实例

对于D类,情况要复杂一些:

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

对于F:

+-------------------------------------------------------+
|                           F                           |
+-------------------------------------------------------+
                    |                              |
                    V                              V
+---------------------------------------+     +---------+
|                   D                   |     |    E    |
+---------------------------------------+     +---------+
       |                         |                 |
       V                         V                 |
+--------------+         +--------------+          |   
|      B       |         |       C      |          |
+--------------+         +--------------+          |
       |                         |                 |
       V                         V                 V
+---------------------------------------+     +---------+
|                   A                   |     |    A    |
+---------------------------------------+     +---------+
这意味着当你创建一个F时,你有:一个D的实例和一个E的实例。由于E实际上不是从a继承的,所以在创建E时会创建一个新的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); } }
class A{virtual void f()=0;}

B类:公共A{virtualvoidf(int值){std::cout采用以下层次结构:

  • 基类
    A
  • B
    C
    E
    继承自
    A
  • D
    继承自
    B
    C
  • F
    继承自
    D
    E
用密码说:

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?
对于这种情况,您可以使用虚拟继承,创建“菱形”:

或者,在代码中:

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::a
C::a
共享相同的内存,但在另一个级别上仍然存在相同的问题:

F f;
f.a = 10; // ERROR: D::a or E::a ?
这一部分我不确定:您也可以使用A for E的
虚拟继承来解决这个问题。但我将保持原样,以回答另一点:混合虚拟继承和非虚拟继承

但请考虑<代码> E::< <代码> > <代码> f>代码>具有不同的值<<代码> d::从同一代码< f>代码> >“,您必须键入您的<代码> f<代码>:

F *f = new F;
(static_cast<D*>(f))->a = 10;
(static_cast<E*>(f))->a = 20;
可以绘制以下内存图:

A类:

+---------+
|    A    |
+---------+
对于B、C和E类:

+---------+
|    B    |
+---------+
     |
     V
+---------+
|    A    |
+---------+
这意味着,对于您创建的每个B、C和E实例,您都会创建另一个A实例

对于D类,情况要复杂一些:

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

对于F:

+-------------------------------------------------------+
|                           F                           |
+-------------------------------------------------------+
                    |                              |
                    V                              V
+---------------------------------------+     +---------+
|                   D                   |     |    E    |
+---------------------------------------+     +---------+
       |                         |                 |
       V                         V                 |
+--------------+         +--------------+          |   
|      B       |         |       C      |          |
+--------------+         +--------------+          |
       |                         |                 |
       V                         V                 V
+---------------------------------------+     +---------+
|                   A                   |     |    A    |
+---------------------------------------+     +---------+
这意味着当你创建一个F时,你有:一个D的实例和一个E的实例。由于E实际上不是从a继承的,所以在创建E时会创建一个新的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); } }
class A{virtual void f()=0;}

B类:公共A{virtualvoidf(int值){std::cout采用以下层次结构:

  • 基类
    A
  • B
    C
    E
    继承自
    A
  • D
    继承自
    B
    C
  • F
    继承自
    D
    E
用密码说:

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?
对于这种情况,您可以使用虚拟继承,创建“菱形”:

或者,在代码中:

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::a
C::a
共享相同的内存,但同样的问题仍然存在