国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

C++語言--多態(tài)性-8.1----多重繼承、虛基類、虛函數(shù)和純虛函數(shù)

2019-11-09 17:08:25
字體:
供稿:網(wǎng)友

1.多重繼承

 一個例子

123456789101112131415161718192021222324252627282930313233#include <iostream>using namespace std; class Base1{public:    virtual void foo1() {};}; class Base2{public:    virtual void foo2() {};}; class MI : public Base1,public Base2{public:    virtual void foo1 () {cout << "MI::foo1" << endl;}    virtual void foo2 () {cout << "MI::foo2" << endl;}}; int main(){    MI oMI;     Base1* pB1 =  &oMI;    pB1->foo1();       Base2* pB2 = (Base2*)(pB1); // 指針強(qiáng)行轉(zhuǎn)換,沒有偏移    pB2->foo2();         pB2 = dynamic_cast<Base2*>(pB1);// 指針動態(tài)轉(zhuǎn)換,dynamic_cast幫你偏移    pB2->foo2();     return 0;}

你會認(rèn)為屏幕上會輸出什么?是下面的結(jié)果嗎?

MI::foo1

MI::foo2

MI::foo2

這樣認(rèn)為沒有什么不對的,因?yàn)镃++的多態(tài)性保證用父類指針可以正確的找到子類實(shí)現(xiàn),并調(diào)用。所以會有上面的輸出。

但是,現(xiàn)實(shí)卻不是這樣,下面是真實(shí)的輸出:

clip_image002

 

為什么

為什么會出現(xiàn)上面的情況呢,上面代碼中的注釋部分也許解釋了,這里再來詳細(xì)的來分析一下。

首先,C++使用一種稱之為vtable(google “vtable” for more details)的東西實(shí)現(xiàn)virtual函數(shù)多態(tài)調(diào)用。vtable每個類中都有一個,該類的所有對象公用,由編譯器幫你生成,只要有virtual函數(shù)的類,均會有vtable。在繼承過程中,由于類Base1和類Base2都有vtable,所以類MI繼承了兩個vtable。簡單的分析一下對象oMI內(nèi)存結(jié)構(gòu),如下:

0 vtable_address_for_Base1 –> [MI::foo1, NULL]

4 vtable_address_for_Base2 –> [MI::foo2, NULL]

其實(shí)很簡單,就兩個vtable的指針,0和4代表相對地址,指針地址大小為4。

pB1的值為0(pB1 == 0),所以調(diào)用“pB1->foo1()”時,可以正確的找到MI::fool這個函數(shù)執(zhí)行。

但是當(dāng)使用強(qiáng)行轉(zhuǎn)換,將pB1轉(zhuǎn)給pB2,那么實(shí)質(zhì)上pB2的值也是0(pB2 == 0),當(dāng)調(diào)用“pB2->foo2()”時,無法在第一個vtalbe中找到對應(yīng)的函數(shù),但是卻不報錯,而是選擇執(zhí)行函數(shù)MI::foo1,不知道為什么會有這種行為,但是這種行為卻十分惡心,導(dǎo)致結(jié)果無法預(yù)期的(最后調(diào)用的函數(shù)會與函數(shù)申明的循序有關(guān)),不太會引起注意,使得bug十分隱晦。

可以設(shè)想,當(dāng)一個有復(fù)雜的業(yè)務(wù)邏輯的程序,而類似這種函數(shù)調(diào)用和指針強(qiáng)行轉(zhuǎn)換分布在不同的函數(shù)或模塊中,可想而知,bug定位十分困難。

當(dāng)使用動態(tài)轉(zhuǎn)換時,也就是“pB2 = dynamic_cast<Base2*>(pB1)”,dynamic_cast函數(shù)會根據(jù)尖括號中的類型進(jìn)行指針偏移,所以pB2的值為4(pB2 == 4),這樣調(diào)用“pB2->foo2()”就會按照期望的方式執(zhí)行。

結(jié)論

上面的現(xiàn)象在單繼承中是不會出現(xiàn)的,因?yàn)橹挥幸粋€vtable(子類的virtual函數(shù)會自動追加到第一個父類的vtable的結(jié)尾)。所以不會出現(xiàn)上面的現(xiàn)象,而多重繼承卻出現(xiàn)了上面的想象,所以需要注意以下兩點(diǎn):

1. 多重繼承需要慎用

2. 類型轉(zhuǎn)換盡量采用c++內(nèi)置的類型轉(zhuǎn)換函數(shù),而不要強(qiáng)行轉(zhuǎn)換

總結(jié):虛繼承,虛函數(shù)都會產(chǎn)生相關(guān)vptrvptr 帶有到基類地址的偏移量通過 dynamic_cast 可以實(shí)現(xiàn)多繼承不同父類之間的轉(zhuǎn)換。

2.虛基類

教科書上面對C++虛基類的描述玄而又玄,名曰“共享繼承”,名曰“各派生類的對象共享基類的的一個拷貝”,其實(shí)說白了就是解決多重多級繼承造成的二義性問題。例如有基類B,從B派生出C和D,然后類F又同時繼承了C和D,現(xiàn)在類F的一個對象里面包含了兩個基類B的對象,如果F訪問自己的從基類B那里繼承過來的的數(shù)據(jù)成員或者函數(shù)成員那么編譯器就不知道你指的到底是從C那里繼承過來的B對象呢還是從D那里繼承過來的B對象。

于是虛基類誕生了,將C和D的繼承方式改為虛繼承,那么F訪問自己從B那里繼承過來的成員就不會有二義性問題了,也就是將F對象里的B對象統(tǒng)一為一個,只有一個基類B對象,下面是一段代碼說明了對虛基類的使用。

#include <iostream>using namespace std;class A{    public:    int i;    void showa(){cout<<"i="<<i<<endl;}};class B:virtual public A      //此處采用虛繼承{    public:    int j;};class C:virtual public A      //此處采用虛繼承{    public:    int k;};class D:public B,public C{    public:    int m;};int main(){    A a;    B b;    C c;    a.i=1;    a.showa();    b.i=2;    b.showa();    c.i=3;    c.showa();    D d;    d.i=4;    d.showa();    //cout << "Hello world!" << endl;    return 0;}

從這個代碼我們可以看出B,C,D從A那里繼承過來了i這個變量并且它們之間不會有任何影響,如果B和C不是虛繼承方式的,那么d.i=4;就不能編譯通過了。

3.虛函數(shù)和純虛函數(shù)

1. 虛函數(shù)和純虛函數(shù)可以定義在同一個類(class)中,含有純虛函數(shù)的類被稱為抽象類(abstract class),而只含有虛函數(shù)的類(class)不能被稱 $

2. 虛函數(shù)可以被直接使用,也可以被子類(sub class)重載以后以多態(tài)的形式調(diào)用,而純虛函數(shù)必須在子類(sub class)中實(shí)現(xiàn)該函數(shù)才可以使用 $

只有聲明而沒有定義。

3. 虛函數(shù)和純虛函數(shù)都可以在子類(sub class)中被重載,以多態(tài)的形式被調(diào)用。

4. 虛函數(shù)和純虛函數(shù)通常存在于抽象基類(abstract base class -ABC)之中,被繼承的子類重載,目的是提供一個統(tǒng)一的接口。

5. 虛函數(shù)的定義形式:virtual    {method body}

    純虛函數(shù)的定義形式:virtual    { } = 0;

    在虛函數(shù)和純虛函數(shù)的定義中不能有static標(biāo)識符,原因很簡單,被static修飾的函數(shù)在編譯時候要求前期bind,然而虛函數(shù)卻是動態(tài)綁定(run-ti$

6. 如果一個類中含有純虛函數(shù),那么任何試圖對該類進(jìn)行實(shí)例化的語句都將導(dǎo)致錯誤的產(chǎn)生,因?yàn)槌橄蠡悾ˋBC)是不能被直接調(diào)用的。必須被子類 $

以下為一個簡單的虛函數(shù)和純虛寒?dāng)?shù)的使用演示!

#include<iostream>using namespace std;//father classclass Virtualbase{public:         virtual void Demon()= 0; //PRue virtual function         virtual void Base() {cout<<"this is farther class"<<endl;}

//sub classclass SubVirtual :public Virtualbase{public:    void Demon()     {          cout<<" this is SubVirtual!" << endl;     }    void Base()    {         cout<<"this is subclass Base"<<endl;     }}/* instance class and sample */int main(){        Virtualbase* inst = new SubVirtual(); //multstate pointer        inst->Demon();        inst->Base();//     inst = new Virtualbase();// inst->Base()       return 0;}


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表

圖片精選

主站蜘蛛池模板: 盐边县| 黄梅县| 谷城县| 商水县| 司法| 万安县| 宣恩县| 静海县| 云霄县| 丰县| 台安县| 旅游| 浦县| 房山区| 丹阳市| 安泽县| 营山县| 文化| 清原| 盐城市| 许昌市| 巴马| 满城县| 家居| 鄂温| 罗城| 贺州市| 北票市| 乌海市| 乌恰县| 平谷区| 鄄城县| 苏尼特左旗| 弥渡县| 长垣县| 乌鲁木齐市| 济南市| 淮阳县| 仙游县| 邓州市| 新建县|