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

c++ 虛繼承,多繼承相關(guān)總結(jié)

 更新時(shí)間:2021年03月01日 10:53:55   作者:程序員楊小哥  
這篇文章主要介紹了c++虛繼承,多繼承相關(guān)總結(jié),幫助大家更好的理解和學(xué)習(xí)使用c++,感興趣的朋友可以了解下

看這一篇文章之前強(qiáng)烈建議先看以下我之前發(fā)布的

虛指針,虛函數(shù)剖析

例1: 以下代碼輸出什么?

#include <iostream>
using namespace std;


class A 
{
protected:
 int m_data;
public:
 A(int data = 0) {m_data=data;}
 int GetData() { return doGetData(); }
 virtual int doGetData() { return m_data; }
};

class B : public A
{
protected:
 int m_data;
public:
 B(int data = 1) { m_data = data; }
 int doGetData() { return m_data; }
};

class C: public B
{
protected:
 int m_data;
public:
 C(int data=2) { m_data = data; }
};

int main(int argc, char const *argv[])
{
 C c(10);

 cout << c.GetData() << endl;
 cout << c.A::GetData() << endl;
 cout << c.B::GetData() << endl;
 cout << c.C::GetData() << endl;
 cout << c.doGetData() << endl;
 cout << c.A::doGetData() << endl;
 cout << c.B::doGetData() << endl;
 cout << c.C::doGetData() << endl;
 return 0;
}

構(gòu)造函數(shù)從最初始的基類開(kāi)始構(gòu)造,各個(gè)類的同名變量沒(méi)有形成覆蓋,都是單獨(dú)的變量。

理解這兩個(gè)重要的C++特性后解決這個(gè)問(wèn)題就比較輕松了。 下面我們?cè)斀膺@幾條輸出語(yǔ)句。

cout << c.GetData() << endl; 本來(lái)是要調(diào)用C類的 GetData(), C中未定義, 故調(diào)用 B 中的, 但是 B 中也未定義, 故調(diào)用 A 中的 GetData(), 因?yàn)?A 中的 doGetData()是虛函數(shù),所以調(diào)用 B 類中的 doGetData(),而 B 類的 doGetData() 返回 B::m_data, 故輸出1。

cout << c.A::GetData() << endl; 因?yàn)?A 中的 doGetData() 是虛函數(shù),又因?yàn)?C 類中未重定義該接口,所以調(diào)用 B 類中的 doGetData(), 而 B 類的 doGetData() 返回 B::m_data, 故輸出 l 。

cout << c.B::GetData() << endl; C調(diào)用哪一個(gè)GetData() 本質(zhì)上都是調(diào)用的A::GetData(), 調(diào)用到 doGetData() 虛函數(shù),再調(diào)用父類B覆蓋后的虛函數(shù),返回B::m_data, 所以前5個(gè)都是1

cout << c.A::doGetData() << endl; 顯示調(diào)用A::doGetData(), 返回 A::m_data, 是0

cout << c.B::doGetData() << endl;, cout << c.C::doGetData() << endl; 都將調(diào)用B::doGetData(), 返回B::m_data, 是1

所以結(jié)果為: 1 1 1 1 1 0 1 1

方便排版,請(qǐng)忽略掉換行。

最后附上內(nèi)存結(jié)構(gòu)圖:

例2: 為什么虛函數(shù)效率低?

因?yàn)樘摵瘮?shù)需要一次間接的尋址,而普通的函數(shù)可以在編譯時(shí)定位到函數(shù)的地址,虛函數(shù)是要根據(jù)虛指針定位到函數(shù)的地址。多增加了一個(gè)過(guò)程,效率肯定低一些,但帶來(lái)了運(yùn)行時(shí)的多態(tài)。

C++支持多重繼承,從而大大增強(qiáng)了面向?qū)ο蟪绦蛟O(shè)計(jì)的能力。多重繼承是一個(gè)類從多個(gè)基類派生而來(lái)的能力,派生類實(shí)際上獲取了所有基類的特性。當(dāng)一個(gè)類是兩個(gè)或多個(gè)基類的派生類時(shí),必須在派生類名和冒號(hào)之后,列出所有基類的類名,基類間用逗號(hào)隔開(kāi)。 派生類的構(gòu)造函數(shù)必須激活所有基類的構(gòu)造函數(shù),并把相應(yīng)的參數(shù)傳遞給它們。派生類可以是另一個(gè)類的基類,這樣,相當(dāng)于形成了一個(gè)繼承鏈。當(dāng)派生類的構(gòu)造函數(shù)被激活時(shí),它的所有基類的構(gòu)造函數(shù)也都會(huì)被激活。

在面向?qū)ο蟮某绦蛟O(shè)計(jì)中,繼承和多重繼承一般指公共繼承。 在無(wú)繼承的類中,protected 和 private 控制符是沒(méi)有差別的,在繼承中,基類的 private 對(duì)所有的外界都屏蔽(包括自己的派生類), 基類的 protected 控制符對(duì)應(yīng)用程序是屏蔽的, 但對(duì)其派生類是可訪問(wèn)的。

虛繼承

什么是虛繼承?它與一般的繼承有什么不同?它有什么用?

虛擬繼承是多重繼承中特有的概念。 虛擬基類是為解決多重繼承而出現(xiàn)的。 請(qǐng)看下圖:

類D繼承自類B和類C, 而類B和類C都繼承自類A.

在類D中會(huì)兩次出現(xiàn)A。為了節(jié)省內(nèi)存空間,可以將B、C對(duì)A 的繼承定義為虛擬繼承,而A就成了虛擬基類。 最后形成如下圖所示的情況:

代碼如下:
class A; 
class B : public virtual A;
class C : public virtual A;
class D : public B, public C;

注意: 虛函數(shù)繼承和虛繼承是完全不同的兩個(gè)概念.

例3: 請(qǐng)?jiān)u價(jià)多重繼承的優(yōu)點(diǎn)和缺陷。

多重繼承在語(yǔ)言上并沒(méi)有什么很?chē)?yán)重的問(wèn)題,但是標(biāo)準(zhǔn)本身只對(duì)語(yǔ)義做了規(guī)定,而對(duì)編譯器的細(xì)節(jié)沒(méi)有做規(guī)定。所以在使用時(shí)(即使是繼承),最好不要對(duì)內(nèi)存布局等有什么假設(shè)。為了避免由此帶來(lái)的復(fù)雜性,通常推薦使用復(fù)合。

  1. 多重繼承本身并沒(méi)有問(wèn)題,不過(guò)大多數(shù)系統(tǒng)的類層次往往有一個(gè)公共的基類,而這樣的結(jié)構(gòu)如果使用多重繼承,稍有不慎,將會(huì)出現(xiàn)一個(gè)嚴(yán)重現(xiàn)象————菱形繼承,這樣的繼承方式會(huì)使得類的訪問(wèn)結(jié)構(gòu)非常復(fù)雜。 但并非不可處理,可以用virtual繼承(并非唯一的方法)
  2. 從哲學(xué)上來(lái)說(shuō),C++多重繼承必須要存在,這個(gè)世界本來(lái)就不是單根的。從實(shí)際用途上來(lái)說(shuō),多重繼承不是必需的。
  3. 多重繼承在面向?qū)ο罄碚撝胁⒎鞘潜匾摹驗(yàn)樗惶峁┬碌恼Z(yǔ)義,可以通過(guò)單繼承與復(fù)合結(jié)構(gòu)來(lái)取代。 而Java則放棄了多重繼承,使用簡(jiǎn)單的interface取代。 因?yàn)镃++中沒(méi)有interface這個(gè)關(guān)鍵字,所以不存在所謂的“接口”技術(shù)。但是C++可以很輕松地做到這樣的模擬,因?yàn)镃++中的不定義屬性的抽象類就是接口。
  4. 多重繼承本身并不復(fù)雜,對(duì)象布局也不混亂,語(yǔ)言中都有明確的定義。真正復(fù)雜的是使用了運(yùn)行時(shí)多態(tài)(virtual)的多重繼承(因?yàn)檎Z(yǔ)言對(duì)于多態(tài)的實(shí)現(xiàn)沒(méi)有明確的定義)。
  5. 要了解C++,就要明白有很多概念是C++ 試圖考慮但是最終放棄的設(shè)計(jì)。你會(huì)發(fā)現(xiàn)很多Java、C#中的東西都是C++考慮后放棄的。

不是說(shuō)這些東西不好,而是在C++中它將破壞C++作為一個(gè)整體的和諧性,或者C++ 并不需要這樣的東西。

舉個(gè)例子來(lái)說(shuō)明,C#中有一個(gè)關(guān)鍵字base用來(lái)表示該類的父類,C++卻沒(méi)有對(duì)應(yīng)的關(guān)鍵字。為什么沒(méi)有?其實(shí)C++中曾經(jīng)有人提議用一個(gè)類似的關(guān)鍵字 inherited, 來(lái)表示被繼承的類,即父類。 這樣一個(gè)好的建議為什么沒(méi)有被采納呢?因?yàn)檫@樣的關(guān)鍵字既不必須又不充分。 不必須是因?yàn)?C++有一個(gè) typedef* inherited,不充分是因?yàn)橛卸鄠€(gè)基類,你不可能知道 inherited 指的是哪個(gè)基類。

例4: 在多繼承的時(shí)候,如果一個(gè)類繼承同時(shí)繼承自 class A 和 class B, 而 class A 和 B 中都有一個(gè)函數(shù)叫 foo(), 如何明確地在子類中指出調(diào)用是哪個(gè)父類的 foo()?

class A
{
public:
 void foo() { cout << "A foo" << endl; }
};

class B
{
public:
 void foo() { cout << "B foo" << endl; }
};

class C : public A, public B
{

};

int main(int argc, char const* argv[])
{
 C c;
 c.A::foo();
 return 0;
}

C 繼承自 A 和 B, 如果出現(xiàn)了相同的函數(shù)foo(), 那么C.A::foo(), C.B::foo() 就分別代表從 A 類中繼承的 foo 函數(shù)和從 B 類中繼承的 foo 函數(shù)。

例5: 以下代碼輸出什么?

class A
{
 int m_nA;
};

class B
{
 int m_nB;
};

class C : public A, public B
{
 int m_nC;
};

int main(int argc, char const* argv[])
{
 C* pC = new C;
 B* pB = dynamic_cast<B*>(pC);
 A* pA = dynamic_cast<A*>(pC);
 cout << (pC == pB) << endl;
 cout << (pC == pA) << endl;
 cout << ((int)pC == (int)pB) << endl;
 cout << ((int)pC == (int)pA) << endl;
 return 0;
}

當(dāng)進(jìn)行pC=pB比較時(shí),實(shí)際上是比較pC指向的對(duì)象和隱式轉(zhuǎn)換pB后pB 指向的對(duì)象 (pC指向的對(duì)象)的部分,這個(gè)是同一部分,是相等的。

但是,pB實(shí)際上指向的地址是對(duì)象C中的父類B部分,從地址上跟pC不一樣,所以直接比較地址數(shù)值的時(shí)候是不相等的。

內(nèi)存結(jié)構(gòu)圖如下:

例6: 如果鳥(niǎo)是可以飛的,那么駝鳥(niǎo)是鳥(niǎo)么?駝鳥(niǎo)如何繼承鳥(niǎo)類?

鳥(niǎo)是可以飛的。 也就是說(shuō),當(dāng)鳥(niǎo)飛行時(shí),它的高度是大于0的。 駝鳥(niǎo)是鳥(niǎo)類(生物學(xué)上)的一種, 但它的飛行高度為0(駝鳥(niǎo)不能飛)。

不要把可替代性和子集相混淆。 即使駝鳥(niǎo)集是鳥(niǎo)集的一個(gè)子集(每個(gè)駝鳥(niǎo)集都在鳥(niǎo)集內(nèi)),但并不意味著鴕鳥(niǎo)的行為能夠代替鳥(niǎo)的行為。 可替代性與行為有關(guān),與子集沒(méi)有關(guān)系。 當(dāng)評(píng)價(jià)一個(gè)潛在的繼承關(guān)系時(shí),重要的因素是可替代的行為,而不是子集。

如果一定要讓駝鳥(niǎo)來(lái)繼承鳥(niǎo)類, 可以采取組合的辦法, 把鳥(niǎo)類中的可以被駝鳥(niǎo)繼承的函數(shù)挑選出來(lái),這樣駝鳥(niǎo)就不是"a kind of"鳥(niǎo)了,而是"has some kind of"鳥(niǎo)的屬性而已。

class bird
{
public:
 void eat();
 void sleep();
 void fly();
};

class ostrich
{
public:
 void eat();
 void sleep();
};

例7: C++中如何阻止一個(gè)類被實(shí)例化?

使用抽象類,或者構(gòu)造函數(shù)被聲明成private。

最后補(bǔ)充兩個(gè)知識(shí)點(diǎn):

函數(shù)的隱藏和覆蓋

  • 函數(shù)的隱藏: 沒(méi)有定義多態(tài)的情況下,即沒(méi)有加virtual的前提下,如果定義了父類和子類,父類和子類出現(xiàn)了同名的函數(shù),就稱子類的函數(shù)把同名的父類的函數(shù)給隱藏了。
  • 函數(shù)的覆蓋:是針對(duì)多態(tài)來(lái)說(shuō)的。 如果定義了父類和子類,父類中定義了公共的虛函數(shù),如果此時(shí)子類中沒(méi)有定義同名的虛函數(shù),那么在子類的虛函數(shù)表中將會(huì)寫(xiě)上父類的該虛函數(shù)的函數(shù)入口地址,如果在子類中定義了同名虛函數(shù)的話,那么在子類的虛函數(shù)表中將會(huì)把原來(lái)的父類的虛函數(shù)地址覆蓋掉,覆蓋成子類的虛函數(shù)的函數(shù)地址。

總結(jié): 本文的重點(diǎn)還是承接之前“虛指針,虛表剖析”的內(nèi)容,對(duì)于多重繼承,沒(méi)有探究其內(nèi)存結(jié)構(gòu),并且也不是很好弄清楚,其功能大多數(shù)可以被組合(composition)的方式實(shí)現(xiàn),C++標(biāo)準(zhǔn)沒(méi)有給出編譯器具體的多繼承的實(shí)現(xiàn)細(xì)節(jié),不同的編譯器有不同的做法。

以上就是c++虛繼承,多繼承相關(guān)總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于c++ 繼承的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C++ 虛函數(shù)專題

    C++ 虛函數(shù)專題

    這篇文章主要介紹了C++中虛函數(shù)的知識(shí)點(diǎn),文中配合代碼講解非常細(xì)致,供大家參考和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • 詳解C++中四種類型的轉(zhuǎn)換

    詳解C++中四種類型的轉(zhuǎn)換

    這篇文章主要是想和大家一起聊聊來(lái)C++中的四種類型轉(zhuǎn)換?:const_cast、static_cast、reinterpret_cast和dynamic_cast,感興趣的可以了解一下
    2022-12-12
  • C++遍歷文件夾目錄的方法

    C++遍歷文件夾目錄的方法

    這篇文章主要介紹了C++遍歷文件夾目錄的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解

    StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解

    這篇文章主要介紹了StretchBlt函數(shù)和BitBlt函數(shù)用法案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • 基于OpenCV和C++ 實(shí)現(xiàn)圖片旋轉(zhuǎn)

    基于OpenCV和C++ 實(shí)現(xiàn)圖片旋轉(zhuǎn)

    這篇文章主要介紹了基于OpenCV和C++ 實(shí)現(xiàn)圖片旋轉(zhuǎn),幫助大家更好的利用c++處理圖片,感興趣的朋友可以了解下
    2020-12-12
  • 詳解C語(yǔ)言和Python中的線程混用

    詳解C語(yǔ)言和Python中的線程混用

    這篇文章主要介紹了C和Python中的線程混用的相關(guān)資料,文中講解非常細(xì)致,幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • C++鏈表實(shí)現(xiàn)通訊錄管理系統(tǒng)

    C++鏈表實(shí)現(xiàn)通訊錄管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++鏈表實(shí)現(xiàn)通訊錄管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易學(xué)生管理系統(tǒng)

    C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易學(xué)生管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易學(xué)生管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • C++中Semaphore內(nèi)核對(duì)象用法實(shí)例

    C++中Semaphore內(nèi)核對(duì)象用法實(shí)例

    這篇文章主要介紹了C++中Semaphore內(nèi)核對(duì)象用法實(shí)例,有助于深入了解信號(hào)量(Semaphore)的基本用法,需要的朋友可以參考下
    2014-10-10
  • 詳解C#byte數(shù)組怎么傳入C

    詳解C#byte數(shù)組怎么傳入C

    在本篇內(nèi)容里小編給大家整理了關(guān)于C#byte數(shù)組怎么傳入C的相關(guān)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友們學(xué)習(xí)參考下。
    2019-03-03

最新評(píng)論