天天看點

第53課 被遺棄的多重繼承(上)

問題:

  C++中是否允許一個類繼承自多個父類呢?答案是肯定的,這種現象就是多重繼承。

  多重繼承是C++中一個特有的特性,因為在其他的程式設計語言裡面,如C#、java等語言隻支援單重繼承

  • C++支援編寫多重繼承的代碼
    • 一個子類可以擁有多個父類
    • 子類擁有所有父類的成員變量 
    • 子類繼承所有父類的成員函數
    • 子類對象可以當作任意父類對象使用---指派相容也适用于此

多重繼承的文法規則:

第53課 被遺棄的多重繼承(上)

             多重繼承的本質與單繼承相同!!!

實驗:多重繼承問題一

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 }      

分析多重繼承:

多重繼承的問題一:

第53課 被遺棄的多重繼承(上)

pa、pb指針還是指向一個對象,隻是指向的位置不同----pa指向頭部,pb可能指向尾部------不友善程式開發

實際開發中,判斷兩個指針是不是指向同一個對象,是根據比較兩個指針儲存的位址值判斷。

現在是兩個指針保持的位址值不同,但是确實是指向同一個對象--------沒有方案處理這個問題

 多重繼承的問題二:

第53課 被遺棄的多重繼承(上)

現實生活中一個人可能扮演兩個角色-------(老師,學生)

一個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 }       
第53課 被遺棄的多重繼承(上)

 中間父類加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++支援多重繼承的程式設計方式
  • 多重繼承容易帶來問題
    • 可能出現 “同一個對象的位址不同” 的情況 。
    • 虛繼承可以解決資料備援問題
    • 虛繼承使得架構設計可能出現問題