深刻解析C++中類的多重繼續。本站提示廣大學習愛好者:(深刻解析C++中類的多重繼續)文章只能為提供參考,不一定能成為您想要的結果。以下是深刻解析C++中類的多重繼續正文
C++類的多繼續
在後面的例子中,派生類都只要一個基類,稱為單繼續。除此以外,C++也支撐多繼續,即一個派生類可以有兩個或多個基類。
多繼續輕易讓代碼邏輯龐雜、思緒凌亂,一向備受爭議,中小型項目中較少應用,後來的 Java、C#、PHP 等爽性撤消了多繼續。想疾速進修C++的讀者可以不用細讀。
多繼續的語法也很簡略,將多個基類用逗號離隔便可。例如已聲清楚明了類A、類B和類C,那末可以如許來聲明派生類D:
class D: public A, private B, protected C{ //類D新增長的成員 }
D是多繼續的派生類,它以共有的方法繼續A類,以公有的方法繼續B類,以掩護的方法繼續C類。D依據分歧的繼續方法獲得A、B、C中的成員,肯定各基類的成員在派生類中的拜訪權限。
多繼續下的結構函數
多繼續派生類的結構函數和單繼續類根本雷同,只是要包括多個基類結構函數。如:
D類結構函數名(總參數表列): A結構函數(實參表列), B類結構函數(實參表列), C類結構函數(實參表列){ 新增成員初始化語句 }
各基類的分列次序隨意率性。
派生類結構函數的履行次序異樣為:先挪用基類的結構函數,再挪用派生類結構函數。基類結構函數的挪用次序是依照聲明派生類時基類湧現的次序。
上面的界說了兩個基類,BaseA類和BaseB類,然後用多繼續的方法派生出Sub類。
#include <iostream> using namespace std; //基類 class BaseA{ protected: int a; int b; public: BaseA(int, int); }; BaseA::BaseA(int a, int b): a(a), b(b){} //基類 class BaseB{ protected: int c; int d; public: BaseB(int, int); }; BaseB::BaseB(int c, int d): c(c), d(d){} //派生類 class Sub: public BaseA, public BaseB{ private: int e; public: Sub(int, int, int, int, int); void display(); }; Sub::Sub(int a, int b, int c, int d, int e): BaseA(a, b), BaseB(c, d), e(e){} void Sub::display(){ cout<<"a="<<a<<endl; cout<<"b="<<b<<endl; cout<<"c="<<c<<endl; cout<<"d="<<d<<endl; cout<<"e="<<e<<endl; } int main(){ (new Sub(1, 2, 3, 4, 5)) -> display(); return 0; }
運轉成果:
a=1 b=2 c=3 d=4 e=5
從基類BaseA和BaseB繼續來的成員變量,在 Sub::display() 中都可以拜訪。
定名抵觸
當兩個基類中有同名的成員時,就會發生定名抵觸,這時候不克不及直接拜訪該成員,須要加上類名和域解析符。
假設在基類BaseA和BaseB中都有成員函數 display(),那末上面的語句是毛病的:
Sub obj; obj.display();
因為BaseA和BaseB中都有display(),體系將沒法剖斷究竟要挪用哪個類的函數,所以報錯。
應當像上面如許加上類名和域解析符:
Sub obj; obj.BaseA::display(); obj.BaseB::display();
經由過程這個舉例可以發明:在多重繼續時,從分歧的基類中會繼續一些反復的數據。假如有多個基類,成績會更凸起,所以在設計派生類時要過細斟酌其數據成員,盡可能削減數據冗余。
C++多重繼續的二義性成績
多重繼續可以反應實際生涯中的情形,可以或許有用地處置一些較龐雜的成績,使編寫法式具有靈巧性,然則多重繼續也惹起了一些值得留意的成績,它增長了法式的龐雜度,使 法式的編寫和保護變得絕對艱苦,輕易失足。個中最多見的成績就是繼續的成員同名而發生的二義性(ambiguous)成績。
假如類A和類B中都有成員函數display和數據成員a,類C是類A和類B的直接派生類。分離評論辯論以下3種情形。
1) 兩個基類有同名成員
代碼以下所示:
class A { public: int a; void display(); }; class B { public: int a; void display (); }; class C: public A, public B { public: int b; void show(); };
假如在main函數中界說C類對象cl,並挪用數據成員a和成員函數display :
C cl; cl.a=3; cl.display();
因為基類A和基類B都稀有據成員a和成員函數display,編譯體系沒法辨別要拜訪的是哪個基類的成員,是以法式編譯失足。那末,應當如何處理這個成績呢?可以用基類名來限制:
cl.A::a=3; //援用cl對象中的基類A的數據成員a cl.A::display(); //挪用cl對象中的基類A的成員函數display
假如是在派生類C中經由過程派生類成員函數show拜訪基類A的display和a,可以不 必寫對象名而直接寫
A::a = 3; //指以後對象 A::display();
2) 兩個基類和派生類三者都有同名成員
將下面的C類聲明改成:
class C: public A, public B { int a; void display(); };
假如在main函數中界說C類對象cl,並挪用數據成員a和成員函數display:
C cl; cl.a = 3; cl.display();
此時,法式能經由過程編譯,也能夠正常運轉。請問:履行時拜訪的是哪個類中的成員?謎底是:拜訪的是派生類C中的成員。規矩是:基類的同名成員在派生類中被屏障,成為“弗成見”的,或許說,派生類新增長的同名成員籠罩了基類中的同名成員。是以假如在界說派生類對象的模塊中經由過程對象名拜訪同名的成員,則拜訪的是派生類的成員。請留意:分歧的成員函數,只要在函數名和參數個數雷同、類型相婚配的情形下才產生同名籠罩,假如只要函數名雷同而參數分歧,不會產生同名籠罩,而屬於函數重載。
有些讀者能夠對同名籠罩覺得不年夜好懂得。為了解釋成績,舉個例子,例如把中國作為基類,四川則是中國的派生類,成都則是四川的派生類。基類是絕對籠統的,派生類是絕對詳細的,基類處於外層,具有較普遍的感化域,派生類處於內層,具有部分的感化域。若“中國”類中有均勻溫度這一屬性,四川和成都也都有均勻溫度這一屬性,假如沒有四川和成都這兩個派生類,談均勻溫度明顯是指全國均勻溫度。假如在四川,議論本地的均勻溫度明顯是指四川的均勻溫度;假如在成都,議論本地的均勻溫度明顯是指成都的均勻溫度。這就是說,全國的“均勻溫度”在四川省被四川的“均勻溫度”屏障了,或許說,四川的“均勻溫度”在本地屏障了全國的“均勻溫度”。四川人最關懷的是四川的溫度,固然不願望用全國溫度籠罩四川的均勻溫度。
假如在四川要查全國均勻溫度,必定要聲明:我要查的是全國的均勻溫度。異樣,要在派生類外拜訪基類A中的成員,應指明感化域A,寫成以下情勢:
cl.A::a=3; //表現是派生類對象cl中的基類A中的數據成員a cl.A::display(); //表現是派生類對象cl中的基類A中的成員函數display
3) 類A和類B是從統一個基類派生的
代碼以下所示:
class N { public: int a; void display(){ cout<<"A::a="<<a<<endl; } }; class A: public N { public: int al; }; class B: public N { public: int a2; }; class C: public A, public B { public: int a3; void show(){ cout<<"a3="<<a3<<endl; } } int main() { C cl; //界說C類對象cl // 其他代碼 }
在類A和類B中固然沒有界說數據成員a和成員函數display,然則它們分離從類N繼續了數據成員a和成員函數display,如許在類A和類B中同時存在著兩個同名的數據成員a和成員函數display。它們是N類成員的拷貝。類A和類B中的數據成員a代表兩個分歧的存儲單位,可以分離寄存分歧的數據。在法式中可以經由過程類A和類B的結構函數去挪用基類N的結構函數,分離對類A和類B的數據成員a初始化。
如何能力拜訪類A中從基類N繼續上去的成員呢?明顯不克不及用
cl.a = 3; cl.display();
或
cl.N::a = 3; cl. N::display();
由於如許仍然沒法差別是類A中從基類N繼續上去的成員,照樣類B中從基類N繼續上去的成員。應該經由過程類N的直接派生類名來指出要拜訪的是類N的哪個派生類中的基類成員。如
cl.A::a=3; cl.A::display(); //要拜訪的是類N的派生類A中的基類成員