一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

虛擬繼承

 sun317 2013-03-06

C++中虛擬繼承的概念

為了解決從不同途徑繼承來的同名的數(shù)據(jù)成員在內(nèi)存中有不同的拷貝造成數(shù)據(jù)不一致問題,將共同基類設(shè)置為虛基類。這時從不同的路徑繼承過來的同名數(shù)據(jù)成員在內(nèi)存中就只有一個拷貝,同一個函數(shù)名也只有一個映射。這樣不僅就解決了二義性問題,也節(jié)省了內(nèi)存,避免了數(shù)據(jù)不一致的問題。
class 派生類名:virtual 繼承方式  基類名
virtual是關(guān)鍵字,聲明該基類為派生類的虛基類。
在多繼承情況下,虛基類關(guān)鍵字的作用范圍和繼承方式關(guān)鍵字相同,只對緊跟其后的基類起作用。
聲明了虛基類之后,虛基類在進一步派生過程中始終和派生類一起,維護同一個基類子對象的拷貝。

 

C++虛擬繼承

◇概念:

C++使用虛擬繼承(Virtual Inheritance),解決從不同途徑繼承來的同名的數(shù)據(jù)成員在內(nèi)存中有不同的拷貝造成數(shù)據(jù)不一致問題,將共同基類設(shè)置為虛基類。這時從不同的路徑繼承過來的同名數(shù)據(jù)成員在內(nèi)存中就只有一個拷貝,同一個函數(shù)名也只有一個映射。

 

◇解決問題:

解決了二義性問題,也節(jié)省了內(nèi)存,避免了數(shù)據(jù)不一致的問題。
◇同義詞: 
虛基類(把一個動詞當成一個名詞而已)
當在多條繼承路徑上有一個公共的基類,在這些路徑中的某幾條匯合處,這個公共的基類就會產(chǎn)生多個實例(或多個副本),若只想保存這個基類的一個實例,可以將這個公共基類說明為虛基類。

◇語法:

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

{

...//派生類成員聲明

};

 

◇執(zhí)行順序

首先執(zhí)行虛基類的構(gòu)造函數(shù),多個虛基類的構(gòu)造函數(shù)按照被繼承的順序構(gòu)造;

執(zhí)行基類的構(gòu)造函數(shù),多個基類的構(gòu)造函數(shù)按照被繼承的順序構(gòu)造;

執(zhí)行成員對象的構(gòu)造函數(shù),多個成員對象的構(gòu)造函數(shù)按照申明的順序構(gòu)造;

執(zhí)行派生類自己的構(gòu)造函數(shù);

析構(gòu)以與構(gòu)造相反的順序執(zhí)行;

mark

從虛基類直接或間接派生的派生類中的構(gòu)造函數(shù)的成員初始化列表中都要列出對虛基類構(gòu)造函數(shù)的調(diào)用。但只有用于建立對象的最派生類的構(gòu)造函數(shù)調(diào)用虛基類的構(gòu)造函數(shù),而該派生類的所有基類中列出的對虛基類的構(gòu)造函數(shù)的調(diào)用在執(zhí)行中被忽略,從而保證對虛基類子對象只初始化一次。

在一個成員初始化列表中同時出現(xiàn)對虛基類和非虛基類構(gòu)造函數(shù)的調(diào)用時,虛基類的構(gòu)造函數(shù)先于非虛基類的構(gòu)造函數(shù)執(zhí)行。

 

◇因果:

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

為什么要引入虛擬繼承

虛擬繼承是多重繼承中特有的概念。虛擬基類是為解決多重繼承而出現(xiàn)的。如:類D繼承自類B1、B2,而類B1、B2都繼承自類A,因此在類D中兩次出現(xiàn)類A中的變量和函數(shù)。為了節(jié)省內(nèi)存空間,可以將B1、B2對A的繼承定義為虛擬繼承,而A就成了虛擬基類。實現(xiàn)的代碼如下:

class A

class B1:public virtual A;

class B2:public virtual A;

class D:public B1,public B2;

虛擬繼承在一般的應(yīng)用中很少用到,所以也往往被忽視,這也主要是因為在C++中,多重繼承是不推薦的,也并不常用,而一旦離開了多重繼承,虛擬繼承就完全失去了存在的必要因為這樣只會降低效率和占用更多的空間。

引入虛繼承和直接繼承會有什么區(qū)別呢

由于有了間接性和共享性兩個特征,所以決定了虛繼承體系下的對象在訪問時必然會在時間和空間上與一般情況有較大不同。

時間:在通過繼承類對象訪問虛基類對象中的成員(包括數(shù)據(jù)成員和函數(shù)成員)時,都必須通過某種間接引用來完成,這樣會增加引用尋址時間(就和虛函數(shù)一樣),其實就是調(diào)整this指針以指向虛基類對象,只不過這個調(diào)整是運行時間接完成的。

空間:由于共享所以不必要在對象內(nèi)存中保存多份虛基類子對象的拷貝,這樣較之多繼承節(jié)省空間。虛擬繼承與普通繼承不同的是,虛擬繼承可以防止出現(xiàn)diamond繼承時,一個派生類中同時出現(xiàn)了兩個基類的子對象。也就是說,為了保證這一點,在虛擬繼承情況下,基類子對象的布局是不同于普通繼承的。因此,它需要多出一個指向基類子對象的指針。
??嫉腃++虛擬繼承
第一種情況:      第二種情況:     第三種情況        第四種情況:
class a         class a        class a          class a
{           {           {             {
    virtual void func();     virtual void func();     virtual void func();     virtual void func();
};           };             char x;           char x;
class b:public virtual a  class b :public a     };             };
{           {           class b:public virtual a    class b:public a
    virtual void foo();     virtual void foo();   {              {
};           };             virtual void foo();      virtual void foo();
                       };              };

如果對這四種情況分別求sizeof(a),  sizeof(b)。結(jié)果是什么樣的呢?下面是輸出結(jié)果:(在vc6.0中運行)
第一種:4,12
第二種:4,4
第三種:8,16
第四種:8,8

想想這是為什么呢?

因為每個存在虛函數(shù)的類都要有一個4字節(jié)的指針指向自己的虛函數(shù)表,所以每種情況的類a所占的字節(jié)數(shù)應(yīng)該是沒有什么問題的,那么類b的字節(jié)數(shù)怎么算呢?看“第一種”和“第三種”情況采用的是虛繼承,那么這時候就要有這樣的一個指針vptr_b_a,這個指針叫虛類指針,也是四個字節(jié);還要包括類a的字節(jié)數(shù),所以類b的字節(jié)數(shù)就求出來了。而“第二種”和“第四種”情況則不包括vptr_b_a這個指針,這回應(yīng)該木有問題了吧。

◇二義性:

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

 2: //名稱:blog_virtual_inherit.cpp 
 3: //說明:C++虛擬繼承學習演示 
 4: //環(huán)境: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: //不能這樣使用,會產(chǎn)生二意性,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: //環(huán)境: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: //不能這樣使用,會產(chǎn)生二意性  
     //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關(guān)鍵詞則為虛擬繼承
1: //-----------------------------------------------------

2: //名稱:blog_virtual_inherit.cpp

3: //說明:C++虛擬繼承學習演示

 
 4: //環(huán)境: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: 請按任意鍵繼續(xù). . .

 

◇通過輸出的比較

1.在多繼承情況下,虛基類關(guān)鍵字的作用范圍和繼承方式關(guān)鍵字相同,只對緊跟其后的基類起作用。
2.聲明了虛基類之后,虛基類在進一步派生過程中始終和派生類一起,維護同一個基類子對象的拷貝。
3.觀察類構(gòu)造函數(shù)的構(gòu)造順序,拷貝也只有一份。 
◇與虛函數(shù)關(guān)系 
虛擬繼承與虛函數(shù)有一定相似的地方,但他們之間是絕對沒有任何聯(lián)系的。
再想一次:虛擬繼承,虛基類,虛函數(shù)。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    激情少妇一区二区三区| 一二区不卡不卡在线观看| 高潮日韩福利在线观看| 成年人视频日本大香蕉久久| 出差被公高潮久久中文字幕| 国产精品一区二区丝袜| 国产精品香蕉免费手机视频| 日韩国产亚洲一区二区三区| 国产成人午夜av一区二区 | 欧美大粗爽一区二区三区 | 亚洲综合激情另类专区老铁性| 日韩无套内射免费精品| 人妻中文一区二区三区| 国产又粗又猛又大爽又黄同志| 国产超薄黑色肉色丝袜| 91麻豆精品欧美一区| 好东西一起分享老鸭窝| 国产日韩熟女中文字幕| 美女被后入福利在线观看| 亚洲日本韩国一区二区三区| 免费亚洲黄色在线观看| 国产三级视频不卡在线观看| 欧美色婷婷综合狠狠爱| 久久天堂夜夜一本婷婷| 国产一区麻豆水好多高潮| 在线日韩中文字幕一区| 亚洲中文字幕视频在线观看| 亚洲中文字幕亲近伦片| 午夜福利黄片免费观看| 精品国产日韩一区三区| 午夜成年人黄片免费观看| 亚洲中文字幕人妻av| 国产精品欧美一区二区三区不卡| 91精品国产综合久久精品| 午夜国产成人福利视频| 日韩成人中文字幕在线一区| 极品熟女一区二区三区| 久久精品国产在热亚洲| 男人和女人草逼免费视频| 日本午夜免费观看视频| 日韩免费成人福利在线|