透析C++類對(duì)象的內(nèi)存模型
雖然有許多人對(duì)C++類對(duì)象表示懷疑,但在年復(fù)一年的不斷發(fā)展中,他的使用率也在不斷提高。如過(guò)要想對(duì)C++類對(duì)象充分了解,但前提是要深入理解到底什么是C++類對(duì)象,及他是怎么運(yùn)作的。
首先介紹一下C++中有繼承關(guān)系的類對(duì)象內(nèi)存的布局:在C++中,如果類中有虛函數(shù),那么它就會(huì)有一個(gè)虛函數(shù)表的指針__vfptr,在類對(duì)象最開(kāi)始的內(nèi)存數(shù)據(jù)中。之后是類中的成員變量的內(nèi)存數(shù)據(jù)。
對(duì)于子類,最開(kāi)始的內(nèi)存數(shù)據(jù)記錄著父類對(duì)象的拷貝(包括父類虛函數(shù)表指針和成員變量)。 之后是子類自己的成員變量數(shù)據(jù)。對(duì)于子類的子類,也是同樣的原理。但是無(wú)論繼承了多少個(gè)子類,對(duì)象中始終只有一個(gè)虛函數(shù)表指針。
為了探討C++類對(duì)象的內(nèi)存布局,先來(lái)寫(xiě)幾個(gè)類和函數(shù)首先寫(xiě)一個(gè)基類:然后,我們多種不同的繼承情況來(lái)研究子類的內(nèi)存對(duì)象結(jié)構(gòu)。
1. 無(wú)虛函數(shù)集繼承
- //子類1,無(wú)虛函數(shù)重載
- class Child1 : public Base
- {
- public:
- virtual void f1() { cout << "Child1::f1" << endl; }
- virtual void g1() { cout << "Child1::g1" << endl; }
- virtual void h1() { cout << "Child1::h1" << endl; }
- int child1;
- protected:
- private:
- };
2. 有一個(gè)虛函數(shù)繼承
- //子類1,無(wú)虛函數(shù)重載
- class Child1 : public Base
- {
- public:
- virtual void f1() { cout << "Child1::f1" << endl; }
- virtual void g1() { cout << "Child1::g1" << endl; }
- virtual void h1() { cout << "Child1::h1" << endl; }
- int child1;
- protected:
- private:
- };
虛擬繼承的子類的內(nèi)存結(jié)構(gòu),和普通繼承完全不同。虛擬繼承的子類,有單獨(dú)的虛函數(shù)表, 另外也單獨(dú)保存一份父類的虛函數(shù)表,兩部分之間用一個(gè)四個(gè)字節(jié)的0x00000000來(lái)作為分界。
子類的內(nèi)存中,首先是自己的虛函數(shù)表,然后是子類的數(shù)據(jù)成員,然后是0x0,之后就是父類的虛函數(shù)表,之后是父類的數(shù)據(jù)成員。如果子類沒(méi)有自己的虛函數(shù),那么子類就不會(huì)有虛函數(shù)表,但是子類數(shù)據(jù)和父類數(shù)據(jù)之間,還是需要0x0來(lái)間隔。
因此,在虛擬繼承中,子C++類對(duì)象和父類的數(shù)據(jù),是完全間隔的。存放子類自己的虛函數(shù)表和數(shù)據(jù),中間以0x分界,最后保存父類的虛函數(shù)和數(shù)據(jù)。如果子類重載了父類的虛函數(shù),那么則將子類內(nèi)存中父類虛函數(shù)表的相應(yīng)函數(shù)替換。
【編輯推薦】