關於虛表,我們就要用到乙個關鍵字:virtual,可以修飾函式,也可以修飾類。類的成員函式被virtual修飾之後,就成為了虛函式;修飾類,主要是虛繼承。
在此之前,我們首先要了解乙個概念:物件模型,也就是說,乙個基類形成之後,裡面的成員是怎麼存放的,當派生類繼承基類之後,派生類的成員是怎麼存放的。
我們首先看下面一段**,普通的public繼承:
class base;
class derive:public base
;void test()
當我們調出記憶體,檢視時物件d,並依次給_b,_d賦值,
就可以得到派生類物件d的物件模型:
虛繼承:
繼承中有public,private,protected等繼承方式,虛繼承就是用關鍵字virtual 繼承的方式
class base{
private:
int _b;
public:
void funtest1()
{ cout<
我們在記憶體中,檢視派生類的物件:
發現在派生類的物件的前面4個位元組已經存放了一些資料,這個資料就是偏移量**的位址,我們用這個位址在開啟一塊記憶體視窗,裡面的資料如下:
其中,0x00855800存放的是相對於自己的偏移量,也就是0,而0x00855804裡面存放的是相對於基類的偏移量,此時,我們也給出虛繼承的物件模型:
tip:在虛繼承的前提下,建立派生類物件的時候,編譯器就會在前4個位元組寫入偏移量**的位址,所以:在沒有建構函式的情況的下,編譯器一定給派生類合成乙個建構函式。
虛繼承能夠解決菱形繼承產生的二義性問題,這個等下面虛函式講解完成之後,在做介紹。
虛函式:
我們先來看下面的**:
如上圖:建立物件b之後,物件的位址就是0x00cff788,裡面的前4四個位元組已經存放內容,我們在第二個記憶體視窗開啟,發現位址裡面存放的是也是位址,此時我們在監視視窗檢視base類裡fun函式的位址,和記憶體2裡面的位址相同。
那麼在繼承的關係中,虛函式又有著怎麼樣的儲存呢?
我們先看一段單繼承的**
class b
{public:
virtual void fun1()
{ cout<
我們在建立基類物件和派生類物件之後,使用基類的物件,檢視其虛表中存放的位址,再在監視視窗,檢視對應函式的位址,就可以知道虛表裡面存放的對應的函式是哪乙個。
也就很容易得到函式的物件模型:
派生類的虛表生成:
①現將基類的虛函式拷貝乙份
②如果派生類中對基類中的虛函式進行了重寫,那麼派生類的虛函式將替代相同偏移量位置的基類的虛函式
③如果派生類中新增加自己的新函式,那麼就按照在派生類中宣告的次序,依次存放到虛表中。
但是在多繼承中,虛表就又有一些小的變化,我們先給出多繼承的實現:
其物件模型:
b2::funtest4()
c::funtest5()
b2::funtest6()
總結起來物件c的物件模型如下:
此時派生類的虛表的產生和單繼承的時候產生規則一樣,派生類自己新產生的虛函式,存放在按照繼承順序中的第乙個。
c 虛函式和虛繼承
c 中,多型的實現需要虛函式,而虛函式主要包括兩部分,虛函式指標和虛函式表。基類將自己的一些函式設為虛函式,子類則需要在繼承基類後,重寫或者直接使用從基類的繼承下來的虛函式。基類自己會儲存乙份虛函式表,這個虛函式表含有指向基類虛函式的虛函式指標。當子類繼承基類後,同樣會將基類的虛函式表繼承下來,這樣...
虛函式 虛繼承
include using namespace std class a class b public a class c public b int main 結果是 4,4,4 為什麼?一,在private,protect,public的實際繼承中,派生類和基類擁有相同的虛函式表。但如果是虛繼承,會...
虛函式,虛繼承
1 空類,空類單繼承,空類多繼承的sizeof include using namespace std class base1 class base2 class derived1 public base1 class derived2 public base1,public base2 int m...