問題:
C++中是否允許一個類繼承自多個父類呢?答案是肯定的,這種現象就是多重繼承。
多重繼承是C++中一個特有的特性,因為在其他的程式設計語言裡面,如C#、java等語言隻支援單重繼承
- C++支援編寫多重繼承的代碼
- 一個子類可以擁有多個父類
- 子類擁有所有父類的成員變量
- 子類繼承所有父類的成員函數
- 子類對象可以當作任意父類對象使用---指派相容也适用于此
多重繼承的文法規則:
多重繼承的本質與單繼承相同!!!
實驗:多重繼承問題一
1 #include<iostream>
2 #include<string>
3
4 //多重繼承帶來的問題:(1)多重繼承得到的對象可以有“不同的位址”
5 // (2)多重繼承可能産生多餘的成員
6
7 using namespace std;
8
9 class BaseA
10 {
11 int ma;
12
13 public:
14 BaseA(int a) //構造函數
15 {
16 ma = a;
17 }
18 int getA() //成員函數
19 {
20 return ma;
21 }
22 };
23
24 class BaseB
25 {
26 int mb;
27
28 public:
29 BaseB(int b)
30 {
31 mb = b;
32 }
33 int getB()
34 {
35 return mb;
36 }
37 };
38
39 class Derived : public BaseA, public BaseB
40 {
41 int mc;
42
43 public:
44
45 //單繼承------子類的構造函數------初始化清單構造父類的構造函數
46
47 Derived(int a, int b, int c) : BaseA(a), BaseB(b) //多繼承--子類的構造函數--初始化清單同時調用兩個父類的構造函數
48 {
49 mc = c;
50 }
51
52 int getC()
53 {
54 return mc;
55 }
56
57 void print()
58 {
59 //cout << "ma=" << ma << "," //子類無法直接通路父類的private私有成員-----通過調用父類成員函數(公有的)通路私有成員變量
60 // << "mb=" << mb <<","
61 // << "mc=" << mc <<endl;
62
63
64 //調用父類成員函數--------通路成員變量
65 cout << "ma=" << getA() << ","
66 << "mb=" << getB() << ","
67 << "mc" << mc << endl;
68 }
69 };
70
71 int main()
72 {
73 cout << "sizeof(Derived)= " << sizeof(Derived) << endl; // 3*4=12 子類記憶體空間大小:父類與子類資料成員的疊加ma mb mc
74 //增加子類成員函數後,記憶體空間大小還是12
75 //類裡面成員函數和成員變量是分開存放的,
76 //每個類有自己的成員變量,都有類對象共享類的成員函數
77
78 //繼承構造函數調用順序:先父母(BaseA(a), BaseB(b) ),再客人(成員變量是其他類的對象--調用成員對象的構造函數),最後自己mc = c;
79 Derived d(1, 2, 3);
80
81 d.print(); //ma =1 ,mb =2 ,mc=3
82
83 //繼承兩個父類成員函數
84 cout << "d.getA()=" << d.getA() << endl; //d.getA()=1
85 cout << "d.getB()=" << d.getB() << endl; //d.getB()=2
86 cout << "d.getC()=" << d.getC() << endl; //d.getC()=3
87
88 cout << endl;
89
90 //指派相容---父類指針pa pb同時指向子類對象d(合法)
91 BaseA* pa = &d;
92 BaseB* pb = &d;
93
94 cout << "pa->getA()=" << pa->getA() << endl; //pa->getA()=1
95 cout << "pb->getB()=" << pb->getB() << endl; //pb->getB()=2
96 cout << endl;
97
98
99 //新定義指針paa pbb同時指向相同對象d
100 void* paa = pa;
101 void* pbb = pb;
102
103 if (paa == pbb) //指針儲存的位址值,
104 {
105 cout << "pointer to the same object! " << endl;
106 }
107 else
108 {
109 cout << "error" << endl; //error
110 }
111
112 cout << "pa= " << pa << endl; //pa=0xbfe7e304
113 cout << "pb= " << pb << endl; //pb=0xbfe7e308
114 cout << "paa= " << paa << endl; //paa=0xbfe7e304
115 cout << "pbb= " << pbb << endl; //pbb=0xbfe7e308
116
117 //父類指針指向子類變量 pa,pb儲存的位址值為什麼不同??????整個期間也沒有改變指針值??
118 //答:多重繼承得到的對象可以有“不同的位址”
119
120
121 return 0;
122 }
分析多重繼承:
多重繼承的問題一:
pa、pb指針還是指向一個對象,隻是指向的位置不同----pa指向頭部,pb可能指向尾部------不友善程式開發
實際開發中,判斷兩個指針是不是指向同一個對象,是根據比較兩個指針儲存的位址值判斷。
現在是兩個指針保持的位址值不同,但是确實是指向同一個對象--------沒有方案處理這個問題
多重繼承的問題二:
現實生活中一個人可能扮演兩個角色-------(老師,學生)
一個Doctor繼承Teacher和Student (Teacher和Student分别擁有m_name、m_age),那麼此時出現Doctor有兩個m_name、m_age---多餘成員産生
實驗:代碼描述上面類圖
1 #include<iostream>
2 #include<string>
3
4 using namespace std;
5
6 class people
7 {
8 string m_name;
9 int m_age;
10 public:
11 people(string name, int age)
12 {
13 m_name = name;
14 m_age = age;
15 }
16 void print()
17 {
18 cout << "name= " << m_name << ","
19 << "age= " << m_age << endl;
20 }
21 };
22
23 class teacher : public people
24 {
25 public:
26 teacher(string name, int age) : people(name, age) //調用父類構造函數
27 {
28 }
29 };
30
31 class student : public people
32 {
33 public:
34 student(string name, int age) : people(name, age)
35 {
36 }
37 };
38
39
40 //多重繼承---------帶來的資料備援
41 class doctor : public student, public teacher
42 {
43 public:
44 //doctor(string name, int age) : student(name, age), teacher(name, age)
45 doctor(string name, int age) : student(name+"1", age+1), teacher(name+"2", age+2)
46 {
47 }
48 };
49
50 int main()
51 {
52 doctor d("mimi", 26);
53
54 //d.print(); //由于doctor繼承了兩個類student和teacher,是以對應doctor有兩個print函數,編譯器不知道調用哪個
55 //使用類作用域分辨符調用print();手動調用
56
57
58 //多重繼承---------帶來的資料備援
59 d.teacher::print(); // name =mimi,age =26
60 d.student::print(); // name =mimi,age =26
61
62 //修改後
63 d.teacher::print(); // name =mimi1,age =27
64 d.student::print(); // name =mimi1,age =27
65
66 return 0;
67 }
中間父類加Virtual關鍵字:
- 虛繼承能夠解決資料備援問題
- 中間層父類不再關心頂層父類的初始化------頂層父類的初始化由子類完成
- 最終子類必須直接調用頂層父類的構造函數
虛繼承發生----Teacher和Student都不再調用父類People的構造函數,而是讓Doctor調用
程式說明:
1 #include<iostream>
2 #include<string>
3
4 //解決多重繼承帶來的資料備援--------->>虛繼承--------中間父類加virtual關鍵字
5 using namespace std;
6
7 class people
8 {
9 string m_name;
10 int m_age;
11 public:
12 people(string name, int age)
13 {
14 m_name = name;
15 m_age = age;
16 }
17 void print()
18 {
19 cout << "name= " << m_name << ","
20 << "age= " << m_age << endl;
21 }
22 };
23
24 class teacher : virtual public people
25 {
26 public:
27 teacher(string name, int age) : people(name, age) //調用父類構造函數
28 {
29 }
30 };
31
32 class student :virtual public people
33 {
34 public:
35 student(string name, int age) : people(name, age)
36 {
37 }
38 };
39
40 class doctor : public student, public teacher
41 {
42 public:
43 doctor(string name, int age) : student(name, age), teacher(name, age) ,people(name,age) //項目工程大的時候找頂層父類是很困難的
44 {
45 }
46 };
47
48 int main()
49 {
50 doctor d("mimi", 26);
51
52 d.print(); // name =mimi,age =26 技術上解決多重繼承帶來的資料備援,但是項目工程大的時候找頂層父類是很困難的
53
54
55 //d.teacher::print(); // name =mimi,age =26
56 //d.student::print(); // name =mimi,age =26
57
58 return 0;
59 }
技術上解決多重繼承帶來的資料備援,但是項目工程大的時候找頂層父類是很困難的-------增加了項目開發時間,代碼複雜性增加
虛繼承在技術方面确實解決了資料備援問題,但是它使程式變得過于複雜,不利于工程方面的應用。
如果在項目中引進多重繼承,當架構設計中需要繼承時,無法确定使用直接繼承還是虛繼承。
注意:多繼承隻适合做學術研究,不适合做工程開發
小結:
- C++支援多重繼承的程式設計方式
- 多重繼承容易帶來問題
- 可能出現 “同一個對象的位址不同” 的情況 。
- 虛繼承可以解決資料備援問題
- 虛繼承使得架構設計可能出現問題