C++虛繼承的概念

峻峰飛陽發表於2017-04-28

http://blog.csdn.net/wangxingbao4227/article/details/6772579

C++中虛擬繼承的概念

為了解決從不同途徑繼承來的同名的資料成員在記憶體中有不同的拷貝造成資料不一致問題,將共同基類設定為虛基類。這時從不同的路徑繼承過來的同名資料成員在記憶體中就只有一個拷貝,同一個函式名也只有一個對映。這樣不僅就解決了二義性問題,也節省了記憶體,避免了資料不一致的問題。
class 派生類名:virtual 繼承方式  基類名
virtual是關鍵字,宣告該基類為派生類的虛基類。
在多繼承情況下,虛基類關鍵字的作用範圍和繼承方式關鍵字相同,只對緊跟其後的基類起作用。
宣告瞭虛基類之後,虛基類在進一步派生過程中始終和派生類一起,維護同一個基類子物件的拷貝。

 

C++虛擬繼承

◇概念:

C++使用虛擬繼承(Virtual Inheritance),解決從不同途徑繼承來的同名的資料成員在記憶體中有不同的拷貝造成資料不一致問題,將共同基類設定為虛基類。這時從不同的路徑繼承過來的同名資料成員在記憶體中就只有一個拷貝,同一個函式名也只有一個對映。

 

◇解決問題:

解決了二義性問題,也節省了記憶體,避免了資料不一致的問題。
 
◇同義詞: 
虛基類(把一個動詞當成一個名詞而已)
當在多條繼承路徑上有一個公共的基類,在這些路徑中的某幾條匯合處,這個公共的基類就會產生多個例項(或多個副本),若只想儲存這個基類的一個例項,可以將這個公共基類說明為虛基類。

 

◇語法:

class 派生類: virtual 基類1,virtual 基類2,...,virtual 基類n

{

...//派生類成員宣告

};

 

◇執行順序

首先執行虛基類的建構函式,多個虛基類的建構函式按照被繼承的順序構造;

執行基類的建構函式,多個基類的建構函式按照被繼承的順序構造;

執行成員物件的建構函式,多個成員物件的建構函式按照申明的順序構造;

執行派生類自己的建構函式;

析構以與構造相反的順序執行;

mark

從虛基類直接或間接派生的派生類中的建構函式的成員初始化列表中都要列出對虛基類建構函式的呼叫。但只有用於建立物件的最派生類的建構函式呼叫虛基類的建構函式,而該派生類的所有基類中列出的對虛基類的建構函式的呼叫在執行中被忽略,從而保證對虛基類子物件只初始化一次。

在一個成員初始化列表中同時出現對虛基類和非虛基類建構函式的呼叫時,虛基類的建構函式先於非虛基類的建構函式執行。

 

◇因果:

多重繼承->二義性->虛擬繼承解決

 

◇二義性:

  1: //-----------------------------------------------------

 2: //名稱:blog_virtual_inherit.cpp 
 3: //說明:C++虛擬繼承學習演示 
 4: //環境:VS2005 
 5: //blog:pppboy.blog.163.com 
 6: //---------------------------------------------------- 
 7: #include "stdafx.h"
 8: #include <iostream>
 9: using namespace std;
 10:
 11: //Base 
 12: class Base
 13: {
 14: public:
 15: Base(){cout << "Base called..."<< endl;}
 16: void print(){cout << "Base print..." <<endl;}
 17: private:
 18: };
 19:
 20: //Sub 
 21: class Sub //定義一個類 Sub 
 22: {
 23: public:
 24: Sub(){cout << "Sub called..." << endl;}
 25: void print(){cout << "Sub print..." << endl;}
 26: private:
 27: };
 28:
 29: //Child 
 30: class Child : public Base , public Sub //定義一個類Child 分別繼承自 Base ,Sub 
 31: {
 32: public:
 33: Child(){cout << "Child called..." << endl;}
 34: private:
 35: };
 36:
 37: int main(int argc, char* argv[])
 38: {
 39: Child c;
 40:
 41: //不能這樣使用,會產生二意性,VC下error C2385 
 42: //c.print();  
 43:
 44: //只能這樣使用 
 45: c.Base::print();
 46: c.Sub::print();
 47:
 48: system("pause");
 49: return 0;
 50: }

 

◇多重繼承:

 
 1: //-----------------------------------------------------  
 2: //名稱:blog_virtual_inherit.cpp  
 
3: //說明:C++虛擬繼承學習演示  
 4: //環境:VS2005  
 5: //blog:pppboy.blog.163.com  
 6: //----------------------------------------------------  
 7: #include "stdafx.h"  
 8: #include <iostream>
 9: using namespace std;  
 10:
 11: int gFlag = 0;  
 12:
 13: class Base  
 14: {
 15: public:  
 16: Base(){cout << "Base called : " << gFlag++ << endl;}  
 17: void print(){cout << "Base print" <<endl;}  
 18: };
 19:
 20: class Mid1 : public Base  
 21: {
 22: public:  
 23: Mid1(){cout << "Mid1 called" << endl;}  
 24: private:  
 25: };
 26:
 27: class Mid2 : public Base  
 28: {
 29: public:  
 30: Mid2(){cout << "Mid2 called" << endl;}  
 31: };
 32:
 33: class Child:public Mid1, public Mid2  
 34: {
 35: public:  
 36: Child(){cout << "Child called" << endl;}  
 37: };
 38:
 39: int main(int argc, char* argv[])  
 40: {
 41: Child d;
 42:
 43: //不能這樣使用,會產生二意性  
     //d.print();  
 45:
 46: //只能這樣使用  
 47: d.Mid1::print();
 48: d.Mid2::print();
 49:
 50: system("pause");  
 51: return 0;  
 52: }
 53:

//output

 
 Base called : 0
 Mid1 called
 Base called : 1 
 Mid2 called
 Child called
 Base print
 Base print

 

◇虛擬繼承

在派生類繼承基類時,加上一個virtual關鍵詞則為虛擬繼承

 
 1: //-----------------------------------------------------  
 2: //名稱:blog_virtual_inherit.cpp  
 
3: //說明:C++虛擬繼承學習演示  
 4: //環境:VS2005  
 5: //blog:pppboy.blog.163.com  
 6: //----------------------------------------------------  
 7: #include "stdafx.h"  
 8: #include <iostream>
 9: using namespace std;  
 10:
 11: int gFlag = 0;  
 12:
 13: class Base  
 14: {
 15: public:  
 16: Base(){cout << "Base called : " << gFlag++ << endl;}  
 17: void print(){cout << "Base print" <<endl;}  
 18: };
 19:
 20: class Mid1 : virtual public Base  
 21: {
 22: public:  
 23: Mid1(){cout << "Mid1 called" << endl;}  
 24: private:  
 25: };
 26:
 27: class Mid2 : virtual public Base  
 28: {
 29: public:  
 30: Mid2(){cout << "Mid2 called" << endl;}  
 31: };
 32:
 33: class Child:public Mid1, public Mid2  
 34: {
 35: public:  
 36: Child(){cout << "Child called" << endl;}  
 37: };
 38:
 39: int main(int argc, char* argv[])  
 40: {
 41: Child d;
 42:
 43: //這裡可以這樣使用  
 44: d.print();
 45:
 46: //也可以這樣使用  
 47: d.Mid1::print();
 48: d.Mid2::print();
 49:
 50: system("pause");  
 51: return 0;  
 52: }
 53:

//output

 
 1: Base called : 0
 2: Mid1 called
 3: Mid2 called
 4: Child called
 5: Base print
 6: Base print
 7: Base print
 8: 請按任意鍵繼續. . .

 

◇通過輸出的比較

1.在多繼承情況下,虛基類關鍵字的作用範圍和繼承方式關鍵字相同,只對緊跟其後的基類起作用。
2.宣告瞭虛基類之後,虛基類在進一步派生過程中始終和派生類一起,維護同一個基類子物件的拷貝。
3.觀察類建構函式的構造順序,拷貝也只有一份。
 
◇與虛擬函式關係 
虛擬繼承與虛擬函式有一定相似的地方,但他們之間是絕對沒有任何聯絡的。
再想一次:虛擬繼承,虛基類,虛擬函式。

相關文章