非常感激我队友大爹给我的复习资料

菱形继承

多继承(Multiple Inheritance)是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员。尽管概念上非常简单,但是多个基类的相互交织可能会带来错综复杂的设计问题,命名冲突就是不可回避的一个。

多继承时很容易产生命名冲突,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承: A->B->D, A->C->D。

在一个派生类中保留间接基类的多份同名成员,虽然可以在不同的成员变量中分别存放不同的数据,但大多数情况下这是多余的:因为保留多份成员变量不仅占用较多的存储空间,还容易产生命名冲突。假如类 A 有一个成员变量 a,那么在类 D 中直接访问 a 就会产生歧义,编译器不知道它究竟来自 A -->B–>D 这条路径,还是来自 A–>C–>D 这条路径。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/*
多继承 : http://c.biancheng.net/cpp/biancheng/view/238.html
多继承(Multiple Inheritance)是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员。
尽管概念上非常简单,但是多个基类的相互交织可能会带来错综复杂的设计问题,命名冲突就是不可回避的一个。
多继承时很容易产生命名冲突,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,
命名冲突依然有可能发生,比如典型的是菱形继承: A->B->D, A->C->D
在一个派生类中保留间接基类的多份同名成员,虽然可以在不同的成员变量中分别存放不同的数据,但大多数情况下这是多余的:
因为保留多份成员变量不仅占用较多的存储空间,还容易产生命名冲突。假如类 A 有一个成员变量 a,那么在类 D 中直接访问 a
就会产生歧义,编译器不知道它究竟来自 A -->B-->D 这条路径,还是来自 A-->C-->D 这条路径。

为了避免产生冲突, 可以使用虚基类, 也可以:
1) 作用域分辨符标识
2) 使用using加以澄清
*/

// 菱形继承
#include <bits/stdc++.h>
using namespace std;

// 间接基类A
class A{
protected:
int m_a;
public:
void showA() {
cout << m_a << endl;
}
};

// 直接基类B
class B : public A{
protected:
int m_b;
void func();
void funcc() {
cout << "This is BBBB" << endl;
}
};

void B::func(){
cout << "This is BBBB" << endl;
}

// 直接基类C
class C : public A{
protected:
int m_c;
void func();
void funcc() {
cout << "This is CCCC" << endl;
}
public:
using A::m_a;

};

void C::func(){
cout << "This is CCCC" << endl;
}

// 派生类D
class D : public B, public C{
private:
int m_d;
public:
// void setA(int a) {m_a = a;} // 命名冲突, error
void setA(int a) {B::m_a = a;} //作用域分辨符标识,使用间接基类B
// void setA(int a) {C::m_a = a;} //或者使用间接基类C
void setB(int b) {m_b = b;}
void setC(int c) {m_c = c;}
void setD(int d) {m_d = d;}

// 如果希望d.func()的用法不产生二义性, 可以使用using关键字加以澄清
using B::func;
// d.funcc()同理
using C::funcc;
};

int main(void){
D d;
// d.m_a = 3; //error,命名冲突
d.C::m_a = 9; // ok
// d.B::m_a = 3; 会报错
d.func();
d.setA(5);
d.funcc();
return 0;
}

/*
================
This is BBBB
This is CCCC
================
*/

解决方法

为了避免产生冲突,可以使用虚基类,也可以:

  • 作用域分辨符标识
  • 使用using加以澄清