亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解C++語法中的虛繼承和虛基類

 更新時間:2023年09月04日 10:15:15   作者:向陽逐夢  
本文主要介紹了C++語法中的虛繼承和虛基類,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

多繼承(Multiple Inheritance)是指從多個直接基類中產(chǎn)生派生類的能力,多繼承的派生類繼承了所有父類的成員。盡管概念上非常簡單,但是多個基類的相互交織可能會帶來錯綜復(fù)雜的設(shè)計(jì)問題,命名沖突就是不可回避的一個。

多繼承時很容易產(chǎn)生命名沖突,即使我們很小心地將所有類中的成員變量和成員函數(shù)都命名為不同的名字,命名沖突依然有可能發(fā)生,比如典型的是菱形繼承,如下圖所示:

圖1:菱形繼承

類 A 派生出類 B 和類 C,類 D 繼承自類 B 和類 C,這個時候類 A 中的成員變量和成員函數(shù)繼承到類 D 中變成了兩份,一份來自 A-->B-->D 這條路徑,另一份來自 A-->C-->D 這條路徑。

在一個派生類中保留間接基類的多份同名成員,雖然可以在不同的成員變量中分別存放不同的數(shù)據(jù),但大多數(shù)情況下這是多余的:因?yàn)楸A舳喾莩蓡T變量不僅占用較多的存儲空間,還容易產(chǎn)生命名沖突。假如類 A 有一個成員變量 a,那么在類 D 中直接訪問 a 就會產(chǎn)生歧義,編譯器不知道它究竟來自 A -->B-->D 這條路徑,還是來自 A-->C-->D 這條路徑。下面是菱形繼承的具體實(shí)現(xiàn):

//間接基類A
class A{
protected:
    int m_a;
};
//直接基類B
class B: public A{
protected:
    int m_b;
};
//直接基類C
class C: public A{
protected:
    int m_c;
};
//派生類D
class D: public B, public C{
public:
    void seta(int a){ m_a = a; }  //命名沖突
    void setb(int b){ m_b = b; }  //正確
    void setc(int c){ m_c = c; }  //正確
    void setd(int d){ m_d = d; }  //正確
private:
    int m_d;
};
int main(){
    D d;
    return 0;
}

這段代碼實(shí)現(xiàn)了上圖所示的菱形繼承,第 25 行代碼試圖直接訪問成員變量 m_a,結(jié)果發(fā)生了錯誤,因?yàn)轭?B 和類 C 中都有成員變量 m_a(從 A 類繼承而來),編譯器不知道選用哪一個,所以產(chǎn)生了歧義。

為了消除歧義,我們可以在 m_a 的前面指明它具體來自哪個類:

    void seta(int a){ B::m_a = a; }

這樣表示使用 B 類的 m_a。當(dāng)然也可以使用 C 類的:

    void seta(int a){ C::m_a = a; }

虛繼承(Virtual Inheritance)

為了解決多繼承時的命名沖突和冗余數(shù)據(jù)問題,C++ 提出了虛繼承,使得在派生類中只保留一份間接基類的成員。

在繼承方式前面加上 virtual 關(guān)鍵字就是虛繼承,請看下面的例子:

    //間接基類A
    class A{
    protected:
        int m_a;
    };
    //直接基類B
    class B: virtual public A{  //虛繼承
    protected:
        int m_b;
    };
    //直接基類C
    class C: virtual public A{  //虛繼承
    protected:
        int m_c;
    };
    //派生類D
    class D: public B, public C{
    public:
        void seta(int a){ m_a = a; }  //正確
        void setb(int b){ m_b = b; }  //正確
        void setc(int c){ m_c = c; }  //正確
        void setd(int d){ m_d = d; }  //正確
    private:
        int m_d;
    };
    int main(){
        D d;
        return 0;
    }

這段代碼使用虛繼承重新實(shí)現(xiàn)了上圖所示的菱形繼承,這樣在派生類 D 中就只保留了一份成員變量 m_a,直接訪問就不會再有歧義了。虛繼承的目的是讓某個類做出聲明,承諾愿意共享它的基類。

其中,這個被共享的基類就稱為虛基類(Virtual Base Class),本例中的 A 就是一個虛基類。在這種機(jī)制下,不論虛基類在繼承體系中出現(xiàn)了多少次,在派生類中都只包含一份虛基類的成員。

現(xiàn)在讓我們重新梳理一下本例的繼承關(guān)系,如下圖所示:

圖2:使用虛繼承解決菱形繼承中的命名沖突問題

觀察這個新的繼承體系,我們會發(fā)現(xiàn)虛繼承的一個不太直觀的特征:必須在虛派生的真實(shí)需求出現(xiàn)前就已經(jīng)完成虛派生的操作。

在上圖中,當(dāng)定義 D 類時才出現(xiàn)了對虛派生的需求,但是如果 B 類和 C 類不是從 A 類虛派生得到的,那么 D 類還是會保留 A 類的兩份成員。換個角度講,虛派生只影響從指定了虛基類的派生類中進(jìn)一步派生出來的類,它不會影響派生類本身。

在實(shí)際開發(fā)中,位于中間層次的基類將其繼承聲明為虛繼承一般不會帶來什么問題。通常情況下,使用虛繼承的類層次是由一個人或者一個項(xiàng)目組一次性設(shè)計(jì)完成的。對于一個獨(dú)立開發(fā)的類來說,很少需要基類中的某一個類是虛基類,況且新類的開發(fā)者也無法改變已經(jīng)存在的類體系。C++標(biāo)準(zhǔn)庫中的 iostream 類就是一個虛繼承的實(shí)際應(yīng)用案例。

iostream 從 istream 和 ostream 直接繼承而來,而 istream 和 ostream 又都繼承自一個共同的名為 base_ios 的類,是典型的菱形繼承。

此時 istream 和 ostream 必須采用虛繼承,否則將導(dǎo)致 iostream 類中保留兩份 base_ios 類的成員。

圖3:虛繼承在C++標(biāo)準(zhǔn)庫中的實(shí)際應(yīng)用

虛基類成員的可見性

因?yàn)樵谔摾^承的最終派生類中只保留了一份虛基類的成員,所以該成員可以被直接訪問,不會產(chǎn)生二義性。此外,如果虛基類的成員只被一條派生路徑覆蓋,那么仍然可以直接訪問這個被覆蓋的成員。但是如果該成員被兩條或多條路徑覆蓋了,那就不能直接訪問了,此時必須指明該成員屬于哪個類。

以圖2中的菱形繼承為例,假設(shè) A 定義了一個名為 x 的成員變量,當(dāng)我們在 D 中直接訪問 x 時,會有三種可能性:

  • 如果 B 和 C 中都沒有 x 的定義,那么 x 將被解析為 A 的成員,此時不存在二義性。
  • 如果 B 或 C 其中的一個類定義了 x,也不會有二義性,派生類的 x 比虛基類的 x 優(yōu)先級更高。
  • 如果 B 和 C 中都定義了 x,那么直接訪問 x 將產(chǎn)生二義性問題。

可以看到,使用多繼承經(jīng)常會出現(xiàn)二義性問題,必須十分小心。

上面的例子是簡單的,如果繼承的層次再多一些,關(guān)系更復(fù)雜一些,程序員就很容易陷人迷魂陣,程序的編寫、調(diào)試和維護(hù)工作都會變得更加困難,因此我不提倡在程序中使用多繼承,只有在比較簡單和不易出現(xiàn)二義性的情況或?qū)嵲诒匾獣r才使用多繼承,能用單一繼承解決的問題就不要使用多繼承。

也正是由于這個原因,C++ 之后的很多面向?qū)ο蟮木幊陶Z言,例如 Java、C#、PHP 等,都不支持多繼承。

到此這篇關(guān)于詳解C++語法中的虛繼承和虛基類的文章就介紹到這了,更多相關(guān)C++虛繼承和虛基類內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C/C++左旋字符串實(shí)現(xiàn)代碼舉例

    C/C++左旋字符串實(shí)現(xiàn)代碼舉例

    在C/C++語言中沒有專門的字符串變量,通常用字符數(shù)組來存放字符串,下面這篇文章主要給大家介紹了關(guān)于C/C++左旋字符串實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2023-12-12
  • opencv 做人臉識別 opencv 人臉匹配分析

    opencv 做人臉識別 opencv 人臉匹配分析

    opencv 人臉識別通過級聯(lián)分類器對特征的分級篩選來確定是否是人臉,每個節(jié)點(diǎn)的正確識別率很高,但正確拒絕率很低,任一節(jié)點(diǎn)判斷沒有人臉特征則結(jié)束運(yùn)算,宣布不是人臉
    2012-11-11
  • Visual C++中MFC消息的分類

    Visual C++中MFC消息的分類

    標(biāo)準(zhǔn)(窗口)消息:窗口消息一般與窗口內(nèi)部運(yùn)作有關(guān),如創(chuàng)建窗口,繪制窗口,銷毀窗口,通常,消息是從系統(tǒng)發(fā)到窗口,或從窗口發(fā)到系統(tǒng)
    2012-11-11
  • C++實(shí)現(xiàn)多人聊天室

    C++實(shí)現(xiàn)多人聊天室

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)多人聊天室,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • C語言使用四種方法初始化結(jié)構(gòu)體

    C語言使用四種方法初始化結(jié)構(gòu)體

    這篇文章說明了什么是結(jié)構(gòu)體,介紹了結(jié)構(gòu)體的概念和使用優(yōu)點(diǎn),在C語言中如何使用和初始化結(jié)構(gòu)體方法,通過詳細(xì)的代碼展開進(jìn)行說明,希望該篇文章對你有所幫助
    2021-06-06
  • C語言 位域詳解及示例代碼

    C語言 位域詳解及示例代碼

    本文主要介紹C語言 位域的知識,這里整理了相關(guān)資料,并附示例代碼及詳解,有興趣的小伙伴可以參考下
    2016-08-08
  • C++哈希表之閉散列方法的模擬實(shí)現(xiàn)詳解

    C++哈希表之閉散列方法的模擬實(shí)現(xiàn)詳解

    閉散列指(開放定址法)發(fā)生沖突時,如果哈希表沒有被填滿,則表內(nèi)一定還有其他空閑位置,可以把沖突值放到下一個沒有被占用的空余位置上。本文將模擬實(shí)現(xiàn)閉散列方法,需要的可以參考一下
    2022-11-11
  • Qt實(shí)現(xiàn)SqlRelationalTable關(guān)聯(lián)表組件

    Qt實(shí)現(xiàn)SqlRelationalTable關(guān)聯(lián)表組件

    在Qt中我們可以通過拖拽的方式將不同組件放到指定的位置,實(shí)現(xiàn)圖形化開發(fā)極大的方便了開發(fā)效率,本章將重點(diǎn)介紹SqlRelationalTable關(guān)聯(lián)表組件的常用方法及靈活運(yùn)用,感興趣的可以了解一下
    2023-12-12
  • C++ const限定符以及頂層const和底層const的案例詳解

    C++ const限定符以及頂層const和底層const的案例詳解

    這篇文章主要介紹了C++ const限定符以及頂層const和底層const的案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • C++實(shí)現(xiàn)LeetCode(189.旋轉(zhuǎn)數(shù)組)

    C++實(shí)現(xiàn)LeetCode(189.旋轉(zhuǎn)數(shù)組)

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(189.旋轉(zhuǎn)數(shù)組),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07

最新評論