欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解C++語(yǔ)法中的虛繼承和虛基類(lèi)

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

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

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

圖1:菱形繼承

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

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

//間接基類(lèi)A
class A{
protected:
    int m_a;
};
//直接基類(lèi)B
class B: public A{
protected:
    int m_b;
};
//直接基類(lèi)C
class C: public A{
protected:
    int m_c;
};
//派生類(lèi)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 行代碼試圖直接訪問(wèn)成員變量 m_a,結(jié)果發(fā)生了錯(cuò)誤,因?yàn)轭?lèi) B 和類(lèi) C 中都有成員變量 m_a(從 A 類(lèi)繼承而來(lái)),編譯器不知道選用哪一個(gè),所以產(chǎn)生了歧義。

為了消除歧義,我們可以在 m_a 的前面指明它具體來(lái)自哪個(gè)類(lèi):

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

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

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

虛繼承(Virtual Inheritance)

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

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

    //間接基類(lèi)A
    class A{
    protected:
        int m_a;
    };
    //直接基類(lèi)B
    class B: virtual public A{  //虛繼承
    protected:
        int m_b;
    };
    //直接基類(lèi)C
    class C: virtual public A{  //虛繼承
    protected:
        int m_c;
    };
    //派生類(lèi)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)了上圖所示的菱形繼承,這樣在派生類(lèi) D 中就只保留了一份成員變量 m_a,直接訪問(wèn)就不會(huì)再有歧義了。虛繼承的目的是讓某個(gè)類(lèi)做出聲明,承諾愿意共享它的基類(lèi)。

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

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

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

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

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

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

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

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

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

虛基類(lèi)成員的可見(jiàn)性

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

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

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

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

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

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

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

相關(guān)文章

  • C++入門(mén)之實(shí)現(xiàn)十步萬(wàn)度游戲

    C++入門(mén)之實(shí)現(xiàn)十步萬(wàn)度游戲

    這篇文章主要介紹了C++入門(mén)實(shí)現(xiàn)十步萬(wàn)度游戲,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-10-10
  • C++ Template 基礎(chǔ)篇(一):函數(shù)模板詳解

    C++ Template 基礎(chǔ)篇(一):函數(shù)模板詳解

    這篇文章主要介紹了C++ Template函數(shù)模板,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 關(guān)于Visual Studio無(wú)法打開(kāi)源文件

    關(guān)于Visual Studio無(wú)法打開(kāi)源文件"stdio.h"問(wèn)題

    這篇文章主要介紹了關(guān)于Visual Studio無(wú)法打開(kāi)源文件"stdio.h"問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • 如何在 C++ 中實(shí)現(xiàn)一個(gè)單例類(lèi)模板

    如何在 C++ 中實(shí)現(xiàn)一個(gè)單例類(lèi)模板

    這篇文章主要介紹了如何在 C++ 中實(shí)現(xiàn)一個(gè)單例類(lèi)模板,幫助大家更好的理解和學(xué)習(xí)c++編程,感興趣的朋友可以了解下
    2020-10-10
  • c++中數(shù)字與字符串之間的轉(zhuǎn)換方法(推薦)

    c++中數(shù)字與字符串之間的轉(zhuǎn)換方法(推薦)

    下面小編就為大家?guī)?lái)一篇c++中數(shù)字與字符串之間的轉(zhuǎn)換方法(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-09-09
  • C語(yǔ)言putenv()函數(shù)和getenv()函數(shù)的使用詳解

    C語(yǔ)言putenv()函數(shù)和getenv()函數(shù)的使用詳解

    這篇文章主要介紹了C語(yǔ)言putenv()函數(shù)和getenv()函數(shù)的使用詳解,用來(lái)進(jìn)行環(huán)境變量的相關(guān)操作,需要的朋友可以參考下
    2015-09-09
  • 詳解C語(yǔ)言正確使用extern關(guān)鍵字

    詳解C語(yǔ)言正確使用extern關(guān)鍵字

    這篇文章主要介紹了C語(yǔ)言正確使用extern關(guān)鍵字,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • C語(yǔ)言 scanf輸入多個(gè)數(shù)字只能以逗號(hào)分隔的操作

    C語(yǔ)言 scanf輸入多個(gè)數(shù)字只能以逗號(hào)分隔的操作

    這篇文章主要介紹了C語(yǔ)言 scanf輸入多個(gè)數(shù)字只能以逗號(hào)分隔的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12
  • C++/CLI在vs上的安裝和初步使用教程

    C++/CLI在vs上的安裝和初步使用教程

    本文給大家介紹C++/CLI在vs上的安裝和初步使用,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2021-07-07
  • C++小練習(xí)之高性能實(shí)現(xiàn)字符串分割

    C++小練習(xí)之高性能實(shí)現(xiàn)字符串分割

    字符串分割是日常工作中比較常見(jiàn)的基礎(chǔ)函數(shù),通常大家會(huì)使用現(xiàn)成的基礎(chǔ)庫(kù),基礎(chǔ)庫(kù)的性能是否是最佳的,本文主要和大家探討一下如何最大限度的提升字符串分割的性能,希望對(duì)大家有所幫助
    2023-10-10

最新評(píng)論