1
、普通的多繼承情況
需要用類名加兩個冒號
:: 來說明成員所屬的基類。
**如下:
classa
inta;
intx;
};classb
intb;
intx;
};classc : public a, public b
;int_tmain(int argc, _tchar* argv)
我們從成員變數和成員方法兩方面進行觀察,
進入除錯模式:檢視
c物件的內容:
可以看到乙個
c物件實際上包含乙個
a物件,乙個
b物件,然後再加上自身特有的屬性c,從
a繼承來的x和從
b繼承來的
x 並不在同一塊空間中。但是由於他們的識別符號(變數名)一樣,所以要通過所在的基類名來說明。
注意:關於上圖中的內容只是物件的邏輯模型,而不是真實的記憶體模型
pc->a::fun();
009a2df3 mov ecx,dword ptr [pc]
009a2df6 call a::fun (09a1523h)
pc->b::fun();
009a2dfb mov ecx,dword ptr [pc]
009a2dfe add ecx,8
009a2e01 call b::fun (09a152dh)
關於方法的呼叫也是類似。
關於這種編譯器的識別方式,我們稍作了解即可。重點在於跟後面的程式的實現原理。
2、使用虛基類的多重繼承
在看虛基類的例子之前,我們看一下普通的有共同基類的情況
**如下:
classa
inta;
};classb1 : public a
;classb2 : public a
;classc : public b1, public b2
;int_tmain(int argc, _tchar* argv)
先看一下物件的內容,可以發現,它首先是由包含b1和
b2的物件,然後再b1和
b2 中分別包含
a物件。c佔
20個位元組。
然後再看一下方法的呼叫情況:
pc->b1::func();
000b68f2 mov ecx,dword ptr [pc]
000b68f5 call a::func (0b1537h)
pc->b2::func();
000b68fa mov ecx,dword ptr [pc]
000b68fd add ecx,8
000b6900 call a::func (0b1537h)
呼叫的方法都是
a中的方法,但是在找到
a的過程需要明確說明:是通過b1
物件還是通過
b2物件。
我們再看一下使用虛基類的情況:
classa
inta;
};classb1 : virtual public a
;classb2 : virtual public a
;classc : public b1, public b2
;int_tmain(int argc, _tchar* argv)
首先還是先看一下
c物件的內容
發現它在原來的基礎上又多了乙個公共的基類物件
a,此時
c物件占用的空間為
24個位元組。需要注意的是,圖中所示的成員變數只是邏輯意義上的。我們再來看一下記憶體中的實際資料。
b1、b2虛繼承之後會在物件開始新增兩個虛指標。真正從
a繼承來的部分在最後存放。
然後再看方法的呼叫,
pc->b1::func();
00e76950 mov eax,dword ptr [pc]
00e76953 mov ecx,dword ptr [eax]
00e76955 mov edx,dword ptr [pc]
00e76958 add edx,dword ptr [ecx+4]
00e7695b mov ecx,edx
00e7695d call a::func (0e71537h)
pc->func();
00e76962 mov eax,dword ptr[pc]
//eax
儲存物件的首位址
00e76965 mov ecx,dword ptr[eax]
// 把
eax前
4個位元組內容複製到
ecx
00e76967 mov edx,dword ptr[pc]
/ / edx
指向物件首位址
00e7696a add edx,dword ptr [ecx+4]
// [ecx]
處的內容是
00000000, [ecx+4]
處的內容是
00000014
(普通成員所占用的位元組數),相加之後,
edx指向虛基類的首位址。此時,
[edx] = 6
00e7696d mov ecx,edx
00e7696f call a::func (0e71537h)
虛繼承時,成員函式的呼叫方式不發生改變。
綜上所述,在虛繼承時,生成的物件的前四個位元組儲存乙個位址,我們稱它為虛指標,它指向一段儲存當前物件結構資料的記憶體空間。我們可以稱它為虛表,從而找到真正要讀寫和寫入的變數。在後面看了多型的
記憶體結構之後,會更加清楚。
思考題:
classa
inta;
};classb1 : public a
;classb2 : public a
;classc : virtual public b1, virtual public b2
;int_tmain(int argc, _tchar* argv)
這是我測試時,想到的一種情況。現在我們用之前分析的內容來分析一下
c物件是如何存放的,以及虛表中會有哪些內容。
首先,這裡b1和
b2都是普通的繼承。而
c則虛繼承於b1、
b2,那麼b1、
b2是c的虛基類。
c的物件的前四個位元組是乙個虛指標,它指向乙個虛表,虛表中應該存放兩個虛基類物件相對於
c物件首位址的偏移位址。
c物件內容如下:虛指標
cccccccc
(c 的空間,我們沒有初始化
) 05 00 00 00 01 00 00 00(b1
繼承來的內容)
06 00 00 00 02 00 00 00 (b2
繼承來的內容)他們的偏移位址是8和
16(10h)
所以虛表中的內容應該是
00 00 00 00(前4
個位元組為
0,可能是做驗證用的,具體的以後再分析)
08 00 00 00 0a 00 00 00
我們現在來驗證一下
多重繼承 C 中的多重繼承
多重繼承是c 的一項功能,其中乙個類可以從多個類繼承。繼承類的建構函式以它們繼承的相同順序被呼叫。例如,在以下程式中,在a的建構函式之前呼叫b的建構函式。include using namespace std class a class b class c public b,public a not...
C 多重繼承之記憶體儲存
c 之多重繼承 1.c 中class與struct。在c 裡面,class與struct沒有本質的區別,只是class的預設許可權是private,而struct則是public。這個概念也揭示了一點 class和struct在內部儲存結構上是一致的。所以我們可以利用這一點來 class的實現原理。...
多重繼承虛函式表分析
project100.cpp 此檔案包含 main 函式。程式執行將在此處開始並結束。include pch.h include using namespace std 基類1 class base1 virtual voidg 基類2 class base2 virtual voidi 子類 cl...