首先了解:
1.類的繼承:派生類擁有其基類擁有的所有數據成員與成員函數,但訪問屬性不同。
2.類的派生:派生類可以加入新的基類不擁有的數據與函數。
多重繼承:一個類派生出多個類,多個類派生出一個類
性質與規則:
1.聲明方法:class D:public A,private B,protected C{}
2.調用順序:先調用A的構造函數,再到B,再到C。虛基類->非虛基類->子對象->派生類。
問題:
1.二義性問題:派生類與基類中存在名字相同的數據或函數,使得函數無法判斷與調用。
解決方法:
1.在引用數據成員或成員函數時指明其作用域。(類名::數據名)
2.同名覆蓋(函數重載):派生類新增的同名成員將會覆蓋基類中的同名成員,不論有多少個基類,而且實現的途徑是利用派生類的新增成員,既要有同名的,則在直接調用不加類名作用域時就會調用派生類的成員。
3.虛基類(虛擬繼承):讓繼承間接共同基類時只保留一份成員。
Class A
Class B:virtual public A
Class C:virtual public A
Class D:public B,public C
此時,A是B的虛基類,A是C的虛基類。
A類 |
Int data; |
Void fun(); |
B類 |
Int A::data; |
Void A::fun() |
Int data_b; |
C類 |
Int A::data |
Void A::fun() |
Int data_c; |
D類 |
Int B::data; |
Int C::data; |
Int data_b; |
Int data_c; |
Void B::fun() |
Void C::fun() |
Int data_d; |
Void fun_d(); |
原來B與C中都有int A::data,到D中分別變成int B::data和int C:: data。但是,加了虛擬繼承virtual後,B與C中的int A::data就多了一個標誌virtual,變成int virtual A::data,而帶有virtual的成員在D中就會被合併成一個,這就是虛擬繼承的本質。
至於構造函數,與普通繼承派生就會有所不同。B與C實際上是還是普通繼承,所以構造函數寫法不變。而D類中含有A的成員且不重複,其實已經可以把A看作是D的直接基類了(體現了虛基類的”虛”),所以D的構造函數就要調用A的構造函數,由A的構造函數來初始化D中A的數據,而不是從B與C中繼承。
4.虛函數:在基類聲明函數是虛擬的,不是實際存在的,在派生類中才正式定義此函數。作用是允許在派生類中重新定義與基類同名的函數,並且可以通過基類指針或引用來訪問基類和派生類中的同名函數(目標是能夠用同一種方式去調用同一類族中不同類的所有的同名函數)。
問題?
類族,每個類都有一個同名的函數display():A<-B<-C<-D<-E<-F。要想調用不同派生類的同名函數,就要用多個類名或者多個指向派生類的指針如a.display().b.display().....,不方便。
這裏的”同一種方法”是?
使用同一基類的指針或引用,調用同一類族中不同類虛函數,如用A的指針pt指向B或C或D或E或F,pt->display()來調用。
爲什麼使用基類指針可以訪問到派生類的成員?
基類指針指向的是基類部分這是毋庸置疑的,但是基類的同名函數聲明成了virtual,而指針指向的派生類存在同名函數,所以這裏有派生類的同名函數取代的基類的virtual函數變成了基類的一部分(體現虛函數的”虛”),所以使用基類指針卻可以訪問到派生類的成員。
虛函數與函數重載方法的區別?
使用的環境不同。