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