采用以下层次结构:
- 基类
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?
对于这种情况,您可以使用虚拟继承,创建一个“钻石”:
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::a 和C::a 共享相同的内存,但同样的问题仍然存在,在另一个层面:
F f;
f.a = 10; // ERROR: D::a or E::a ?
这部分我不确定Confirmed:你也可以使用virtual 从 A 继承 E 来解决这里的问题。但我将保持原样,以回答另一点:混合虚拟继承和非虚拟继承。
但考虑到您希望来自F 的E::a 与相同的F 具有不同的D::a 值。为此,您必须输入您的F:
F *f = new F;
(static_cast<D*>(f))->a = 10;
(static_cast<E*>(f))->a = 20;
现在您的F* f 拥有两个不同的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 { }
可以画出如下内存图:
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); } }
A 被称为abstract(有些也称为interface),因为有纯虚函数。
B也是abstract,因为它继承自A并且没有覆盖A::f(void)方法,它是纯虚的,甚至定义了自己的方法(B::f(int))
C 是B 的implementation,因为它确实定义了将B 转换为“完整”类所需的所有函数——它覆盖了A::f(void)。
这个答案并不完整,但它给出了一个大致的想法。