国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

C++對象布局及多態(tài)實(shí)現(xiàn)探索之內(nèi)存布局

2019-11-17 05:09:17
字體:
供稿:網(wǎng)友
前言

  本文通過觀察對象的內(nèi)存布局,跟蹤函數(shù)調(diào)用的匯編代碼。分析了C++對象內(nèi)存的布局情況,虛函數(shù)的執(zhí)行方式,以及虛繼續(xù),等等。

  寫這篇文章源于我在論壇上看到的一個貼子。有人問VC使用了哪種方式來實(shí)現(xiàn)虛繼續(xù)。當(dāng)時我寫了一點(diǎn)代碼想驗(yàn)證一下,結(jié)果發(fā)現(xiàn)情況比我想象的要復(fù)雜。所以我就干脆認(rèn)真把相關(guān)的問題都過了一遍,并記錄成本文。

  我對于C++對象模型的知識主要來自于Lippman的書《Inside the C++ Object Model》,中譯版為候捷翻的《深度探索C++對象模型》,中英版我都看過,不過我還是推薦中譯版,因?yàn)橹凶g版的確翻得不錯,而且候捷加入了很多的圖,并修正了原版中的一些錯誤。

  我所使用的編譯器是VC7.1,文中的代碼我都在VC7.1上驗(yàn)證通過。假如在其他的編譯器下運(yùn)行需要作相應(yīng)的調(diào)整,即使是VC7.0和VC6也是如此。不同編譯器產(chǎn)生的匯編代碼也不一樣,假如你在不同編譯器上編譯文中的代碼生成出的匯編代碼和我所列出的不同,也不足為奇。假如你想在其他的編譯器上驗(yàn)證這些代碼請自行做相應(yīng)的改動。

  另外我發(fā)現(xiàn)VC7.1在實(shí)現(xiàn)虛繼續(xù)時所用的方法和Lippman在書中提到的微軟所用的方法不同,不過那時還沒有VC7.1。有趣的是,Lippman在寫那本書時,是在迪斯尼工作,應(yīng)該是做和三維影片的渲染軟件相關(guān)的事。而現(xiàn)在他已經(jīng)到了微軟,相信應(yīng)該是主導(dǎo)VC7.1編譯器的設(shè)計(jì)工作。

  在后文中可以看到列出的很多匯編代碼,有些明顯效率很低。這可能是因?yàn)槲覜]有打開編譯器的優(yōu)化開關(guān)。打開優(yōu)化開關(guān),設(shè)置不同的優(yōu)化選項(xiàng)后,編譯器可能產(chǎn)生出高效得多的匯編代碼。有愛好的朋友可以自行試試,并和文中列出的匯編代碼做一下比較。

  為了便于分析和觀察對象的內(nèi)存布局,我把代碼生成時的結(jié)構(gòu)成員對齊選項(xiàng)設(shè)置為1字節(jié),默認(rèn)為8字節(jié)。假如你在自己的工程下編譯文中的代碼,請做同樣的設(shè)置。因?yàn)槲覍懥艘恍┖瘮?shù)打印對象中的布局信息,假如對象選項(xiàng)不是1字節(jié),運(yùn)行這些代碼會出現(xiàn)指針異常錯誤。

  普通類對象的內(nèi)存布局

  首先我們從普通類對象的內(nèi)存布局開始。C000為一個空類,定義如下:

strUCt C000
{};
  運(yùn)行如下代碼打印它的大小及對象中的內(nèi)容。

PRINT_SIZE_DETAIL(C000)
  結(jié)果為:

The size of C000 is 1
The detail of C000 is cc
  可以看到它的大小為1字節(jié),這是一個占位符。我們可以看到它的值是0xcc。在debug模式下,這表示是由編譯器插入的調(diào)試代碼所初始化的內(nèi)存。在release模式下可能是個隨機(jī)值,我測試時值為0x00。

  定義兩個類,C010和C011如下:

struct C010
{
 C010() : c_(0x01) {}
 void foo() { c_ = 0x02; }
 char c_;
};
struct C011
{
 C011() : c1_(0x02), c2_(0x03) {}
 char c1_;
 char c2_;
};
  運(yùn)行如下代碼打印它們的大小及對象中的內(nèi)容。

PRINT_SIZE_DETAIL(C010)
PRINT_SIZE_DETAIL(C012)
  結(jié)果為:

The size of C010 is 1
The detail of C010 is 01
The size of C011 is 2
The detail of C011 is 02 03
  我們從對象的內(nèi)存輸出中可以看到,它們的值就是我們在構(gòu)造函數(shù)中賦的值,C010為0x01,C011為0x0203。大小分別為1、2。

  定義C012類。

struct C012
{
 static int sfoo() { return 1; }
 int foo() { return 1; }
 char c_;
 static int i_;
};
int C012::i_ = 1;
  在這個類中我們加入了一個靜態(tài)數(shù)據(jù)成員,一個普通成員函數(shù)和一個靜態(tài)成員函數(shù)。

  運(yùn)行如下代碼打印它的大小及對象中的內(nèi)容。

PRINT_SIZE_DETAIL(C012)
  結(jié)果為:

The size of C012 is 1
The detail of C012 is cc
  可以看到它的大小還是1字節(jié),值為0xcc是因?yàn)槲覀儧]有初始化它,原因前面說過了。

  從上面的結(jié)果我們可以映證,普通成員函數(shù),靜態(tài)成員函數(shù),及靜態(tài)成員變量皆不會在類的對象中有所表示,成員函數(shù)和對象的關(guān)聯(lián)由編譯器在編譯時處理,正如我們會在后面看到的那樣,編譯器會在編譯時決議出正確的普通成員函數(shù)地址,并將對象的地址以this指針的方式,做為第一個參數(shù)傳遞給普通成員函數(shù),以此來進(jìn)行關(guān)聯(lián)。靜態(tài)成員函數(shù)類似于全局函數(shù),不和具體的對象關(guān)聯(lián)。靜態(tài)成員變量也一樣。靜態(tài)成員函數(shù)和靜態(tài)成員變量和普通的全局函數(shù)及全局變量不同之處在于它們多了一層名字限定。

  普通繼續(xù)類對象的內(nèi)存布局

  下面看看普通繼續(xù)類對象的內(nèi)存布局。

  定義一個空類C014從C011繼續(xù),再定義C015也是一個空類從C010和C011繼續(xù)。


struct C010
{
 C010() : c_(0x01) {}
 void foo() { c_ = 0x02; }
 char c_;
};
struct C011
{
 C011() : c1_(0x02), c2_(0x03) {}
 char c1_;
 char c2_;
};
struct C014 : private C011
{};
struct C015 : public C010, private C011
{};
  運(yùn)行如下代碼打印它們的大小及對象中的內(nèi)容。

PRINT_SIZE_DETAIL(C014)
PRINT_SIZE_DETAIL(C015)
  結(jié)果為:

The size of C014 is 2
The detail of C014 is 02 03
The size of C015 is 3
The detail of C015 is 01 02 03
  C014的大小為2字節(jié),也就是C011的大小,對象的內(nèi)存值也是在C011的構(gòu)造函數(shù)中初始化的兩個值0x0203。C015的大小為3字節(jié),也就是C010和C011的大小之和,對象的內(nèi)存值為0x010203。

  這里我們可以發(fā)現(xiàn)父類的成員變量悉數(shù)被子類繼續(xù),并且于繼續(xù)方式(公有或私有)無關(guān),如C015是私有繼續(xù)自C011。繼續(xù)方式只影響數(shù)據(jù)成員的“能見度”。子類對象中屬于從父類繼續(xù)的成員變量由父類的構(gòu)造函數(shù)初始化。通常會調(diào)用默認(rèn)構(gòu)造函數(shù),除非子類在它的構(gòu)造函數(shù)初始化列表中顯式調(diào)用父類的非默認(rèn)構(gòu)造函數(shù)。假如沒有指定,而父類又沒有缺省構(gòu)造函數(shù),則會產(chǎn)生編譯錯誤。

  我們可以再加一層繼續(xù)來驗(yàn)證一下。定義類C016,從C015繼續(xù),并有自己的4字節(jié)int成員變量。

struct C016 : C015
{
 C016() : i_(1) {}
 int i_;
};
  運(yùn)行如下代碼打印它的大小及對象中的內(nèi)容。

PRINT_SIZE_DETAIL(C016)
  結(jié)果為:

The size of C016 is 7
The detail of C016 is 01 02 03 01 00 00 00
  它的大小為7字節(jié),也就是C015的大小(也即是C010和C011的大小和)加上自身的4字節(jié)int變量之和。同樣對象的內(nèi)存輸出也驗(yàn)證了這一點(diǎn),前三個字節(jié)為從父類繼續(xù)的,后4個字節(jié)為自身的int變量,值為1。

  因此關(guān)于普通繼續(xù),子類的對象布局為父類中的數(shù)據(jù)成員加上子類中的數(shù)據(jù)成員,多層繼續(xù)時(如C016),頂層類在前,多重繼續(xù)時則最左父類在前。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 桑日县| 博爱县| 汽车| 郸城县| 东安县| 乌苏市| 裕民县| 治多县| 开阳县| 南开区| 桐庐县| 民乐县| SHOW| 获嘉县| 长宁县| 屏东县| 施秉县| 白城市| 南通市| 遵义县| 水城县| 华容县| 正阳县| 滦南县| 新巴尔虎左旗| 金秀| 莱芜市| 绵阳市| 喀喇| 满洲里市| 尉犁县| 色达县| 泸西县| 宜黄县| 长兴县| 东平县| 高邮市| 潞城市| 德江县| 盘山县| 涿州市|