(C/C++學習心得)5.C++中的虛繼承-虛擬函式-多型解析
摘要:
說明 :在C++學習的過程中,虛繼承-虛擬函式經常是初學者容易產生誤解的兩個概念,它們與C++中多型形成的關係,也是很多初學者經常產生困惑的地方,這篇文章將依次分別對三者進行解析,並講述其之間的聯絡與不同。
一.虛繼承
1.在多繼承中,對於多個父類的資料及函式成員,雖然有時候把他們...
說明 :在C++學習的過程中,虛繼承-虛擬函式經常是初學者容易產生誤解的兩個概念,它們與C++中多型形成的關係,也是很多初學者經常產生困惑的地方,這篇文章將依次分別對三者進行解析,並講述其之間的聯絡與不同。
一.虛繼承
1.在多繼承中,對於多個父類的資料及函式成員,雖然有時候把他們全部繼承下來是有必要的,比如當這些成員都不同的時候。但在大多數的情況下,比如當多個父類之中的成員有重疊的地方時,因為保留多份資料成員的拷貝,不僅佔有了較多的儲存空間,還增加了訪問的難度(由於繼承了來自多個父類的同名數據成員,訪問時需要加上父類的作用域,比如“父類名::成員”)。
2.在虛繼承中,被虛繼承的類叫做虛基類,虛基類是需要設計和抽象的,它應當提取多繼承父類中重疊的部分作為成員,虛繼承是對繼承的一種擴充套件。
示例1:
1 #include<iostream> 2 using namespace std; 3 4 class furniture 5 { 6 public: 7furniture(float l,float wi,float we) 8:len(l),wid(wi),weight(we) 9{} 10void dis() 11{ 12cout<<"len = "<<len<<endl; 13cout<<"wid = "<<wid<<endl; 14cout<<"weight="<<weight<<endl; 15} 16 protected: 17float len; 18float wid; 19float weight; 20 }; 21 22 //+++++++++++++++++++++++++ 23 24 class bed:virtual public furniture 25 { 26 public: 27bed(float l,float wi,float we) 28:furniture(l,wi,we) 29{} 30 31void sleep(){cout<<"go to sleep!!!!!"<<endl;} 32 }; 33 34 //+++++++++++++++++++++++++ 35 36 class sofa:virtual public furniture 37 { 38 public: 39sofa(float l,float wi,float we) 40:furniture(l,wi,we) 41{} 42 43void sit(){cout<<"go to have a rest!!!!!"<<endl;} 44 }; 45 46 //+++++++++++++++++++++++++ 47 48 class sofabed:public bed,public sofa 49 { 50 public: 51sofabed(float l,float wi,float we) 52:furniture(l,wi,we),bed(1,2,3),sofa(1,2,3) 53{} 54 }; 55 56 int main() 57 { 58bed b(1,2,3); 59b.sleep(); 60b.dis(); 61sofa s(2,3,4); 62s.sit(); 63s.dis(); 64sofabed sb(4,5,6); 65sb.sleep(); 66sb.sit(); 67sb.dis(); 68return 0; 69 } 70 檢視程式碼
程式執行結果:
在本例中,如果僅僅採用的是多繼承而非虛繼承,如下程式碼所示:
1 #include<iostream> 2 using namespace std; 3 4 class bed 5 { 6 public: 7bed(float l,float wi,float we) 8:len(l),wid(wi),weight(we) 9{} 10 11void sleep() 12{ 13cout<<"go to sleep!!!!!"<<endl; 14} 15 16void dis() 17{ 18cout<<"len = "<<len<<endl; 19cout<<"wid = "<<wid<<endl; 20cout<<"weight = "<<weight<<endl; 21} 22 protected: 23float len; 24float wid; 25float weight; 26 }; 27 //+++++++++++++++++++++++++++++ 28 class sofa 29 { 30 public: 31sofa(float l,float wi,float we) 32:len(l),wid(wi),weight(we) 33{} 34void sit() 35{ 36cout<<"go to have a rest!!!!!"<<endl; 37} 38void dis() 39{ 40cout<<"len = "<<len<<endl; 41cout<<"wid = "<<wid<<endl; 42cout<<"weight = "<<weight<<endl; 43} 44 protected: 45float len; 46float wid; 47float weight; 48 49 }; 50 //+++++++++++++++++++++++++++ 51 class sofabed:public bed,public sofa 52 { 53 public: 54sofabed(float l,float wi,float we) 55:bed(l,wi,we),sofa(l,wi,we) 56{} 57 }; 58 //+++++++++++++++++++++++++++ 59 int main() 60 { 61bed b(1,2,3); 62b.sleep(); 63b.dis(); 64sofa s(2,3,4); 65s.sit(); 66s.dis(); 67sofabed sb(5,6,7); 68sb.sit(); 69sb.sleep(); 70sb.sofa::dis(); 71sb.bed::dis(); 72return 0; 73 } 檢視程式碼
則sb.dis()就有問題了;因為它產生了二義性,編譯器不知道該呼叫哪一個父類的成員函式,而正確做法是加上父類的作用域,這無疑是增加了訪問了難度。
結論:多繼承帶來的資料儲存多份,佔用記憶體空間較多,並且訪問不便(作用域的增加)。
二.純虛擬函式
1.純虛擬函式的格式:
1 class A 2 { 3virtual void func() = 0; 4 }
2.含有純虛擬函式的類為抽象基類,不可建立物件,其存在的意義就是被繼承,提供族類的公共介面,
3.純虛擬函式只有宣告,沒有實現,被初始化為0,
4.如果一個類中聲明瞭純虛擬函式,而在派生類中沒有對該函式定義,則該函式在派生類中仍然為純虛擬函式,派生類仍然為純虛基類,
5.含有虛擬函式的類,解構函式也應該宣告為虛擬函式,這樣在delete父類指標的時候,才會呼叫子類的解構函式,實現完整析構,
1 #include<iostream> 2 using namespace std; 3 4 class A 5 { 6 public: 7A() 8{ 9cout<<"A(){}"<<endl; 10} 11virtual ~A() 12{ 13cout<<"~A(){}"<<endl; 14} 15virtual void func() = 0; 16 }; 17 class B:public A 18 { 19 public: 20B(){cout<<"B(){}"<<endl;} 21~B(){cout<<"~B(){}"<<endl;} 22virtual void func() 23{ 24cout<<"B.func(){}"<<endl; 25} 26 }; 27 int main() 28 { 29A*pa = new B; 30pa->func(); 31delete pa; 32return 0; 33 } 34 檢視程式碼
程式執行結果:
注意: 若在此例中,沒有將含有虛擬函式的父類解構函式宣告為虛擬函式,則將不會呼叫子類的解構函式~B()實現完整析構。
三.多型的實現
1.C++中的多型指的是由於繼承而產生的相關的不同的類,其物件對同一訊息會做出不同的反應。
2.多型實現的前提是賦值相容,賦值相容的內容如下:
a.子類的物件可以賦值給基類的物件,
b.子類的物件可以賦值給基類的引用,
c.子類物件的地址可以賦值給基類的指標(一般用於動多型的實現),
d.在賦值後,子類物件就可以作為基類物件使用,但只能訪問從基類繼承的成員.
3.動多型的實現條件:
a.父類中有虛擬函式,
b.子類override(覆寫)父類中的虛擬函式,
c.將子類的物件賦值給父類的指標或引用,由其呼叫公用介面.
1 #include<iostream> 2 using namespace std; 3 4 class Shape 5 { 6 public: 7virtual void draw() = 0; 8 }; 9 //+++++++++++++++++++ 10 class Circle:public Shape 11 { 12 public: 13void draw() 14{ 15cout<<"Circle"<<endl; 16} 17 }; 18 //+++++++++++++++++++ 19 class Rect:public Shape 20 { 21 public: 22void draw() 23{ 24cout<<"Rect"<<endl; 25} 26 }; 27 int main() 28 { 29Circle c; 30Rect r; 31Shape *p = &c; 32p->draw(); 33p = &r; 34p->draw(); 35return 0; 36 } 37 檢視程式碼