虛繼承的出現就是為了解決多繼承中訪問不明確的問題。
首先讓我們先看一下虛繼承的**:
#include
using
namespace
std;
class aa
};class bb : virtual
public aa
};class cc : virtual
public aa
};class dd : public bb, public cc
};int main()
輸出結果:
dd物件在記憶體中的結構如下:
我們看到虛繼承中,記憶體的分配是與多繼承不同的,我們不再是賦值兩份aa物件,而是將虛基類放在了最後面,然後原來的bb和cc所繼承的aa的位址存放的是指向aa物件的指標,這樣做的另乙個好處是還可以節約記憶體空間,因為如果aa物件的大小是100個位元組,那我們也只需要乙個4位元組的指標指向它就可以了,而不用賦值乙份兒占用空間。
注意:虛基類只有乙份,並且所有繼承虛基類的子類都要虛繼承,否則如果有乙個不是虛繼承的那也會再複製乙份aa,記憶體中同樣存在了兩份aa,那麼訪問不明確的問題就還是存在。
虛基類為什麼要放在最後?
因為我們在使用這個虛基類的時候首先要知道是哪乙個物件(就是指向虛基類的指標)呼叫的它,我們把虛基類放在最後就是要保證:前面的所有的物件使用虛基類的時候都是通過指向虛基類的指標找到的。如果我們不放在後面而是放在前面,那麼找到虛基類就有了兩種方式:直接使用物件的位址 or 指向虛基類的指標,這樣我們在使用虛基類的時候還要加乙個判斷,來判斷到底是誰呼叫的呢?這樣顯然更麻煩了。所以我們要把虛基類放在後面,讓所有子類都是通過指向虛基類的指標來找到它。
我們首先看一道題:下列程式的結果是什麼?
#include
#include
#include
using
namespace
std;
class a;};
class b : public
virtual a;};
class c : public
virtual b;};
int main(int arge,char *argv)
輸出結果:
我們來看一下c物件在記憶體中的結構:
從圖中我們可以看出:每個類最初始的4個位元組都是指向虛函式列表的指標,由於a是虛基類,沒有父類,所以a只有8個位元組大小;而b虛繼承了a,所以b要有乙個指向a類位址的指標,所以b類是12+8(a的大小)=20;同樣,c類虛繼承了b,所以c要有乙個指向b類位址的指標,所以c類是12+20(b的大小)=32。
什麼情況下會產生新的虛函式列表呢?
為了便於理解,我們首先來看一下上乙個例子中,改為普通繼承情況下的輸出:
我們現在再看一下c物件在記憶體中的結構:
那麼我們是通過什麼方式來確定他們的結構的呢?就是分別用不同型別的指標去指向c這個物件。如下:
c c;
a* a = &c;
b* b = &c;
當我們使用普通繼承的時候a、b、c所返回的位址都是相同的,即 0c 位址。所以在實現多型的時候,父類將虛函式加到了這個虛表中,而他的子類在重寫虛函式的時候也是首先複製了父類的虛函式列表,然後用自己重寫的虛函式的指標覆蓋父類所存入的指標。這樣我們在父類呼叫多型的時候就可以直接從虛表裡找到新的虛函式的位址了。 C 知識點53 虛繼承
二 虛繼承 1.概念 預設情況下,c 的派生列表中不允許同乙個基類出現兩次,但是,如果兩個基類都繼承了同乙個類a,那麼兩個基類派生出的子類就會包含兩次類a的部分 為了解決上述問題,c 中就出現了虛繼承,通過虛繼承,無論虛基類在整個繼承鏈 現了多少次,子類中都只出現一次。示例class animal ...
面試知識點總結 虛繼承
背景 儘管在派生列表中同乙個基類只能出現一次,但實際上派生類可以多次繼承同乙個類。派生類可以通過它的兩個直接基類分別繼承同乙個間接基類,也可以直接繼承某個基類,然後通過另乙個基類再一次間接繼承該類。出現的問題 在預設情況下,派生類中含有繼承鏈上每個類對應的子部分。如果某個類在派生過程中出現了多次,則...
C 繼承相關知識點
c 作為物件導向的語言,類之間可以繼承,被繼承的類稱為基類 父類 產生的新類稱為派生類 子類 c 的類許可權分為三個等級,private 私有的 protect 被保護的 public 公有的 其相對應的繼承的許可權也分為相同的三個等級,即private,protect以及public繼承。這三類繼...