青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

統計

  • 隨筆 - 50
  • 文章 - 42
  • 評論 - 147
  • 引用 - 0

留言簿(6)

隨筆分類

文章分類

Link

搜索

  •  

積分與排名

  • 積分 - 167183
  • 排名 - 159

最新評論

閱讀排行榜

評論排行榜

MSVC++ 對象內存模型深入解析與具體應用 (二)

MSVC++ 對象內存模型深入解析與具體應用

前言:本文之所以強調MSVC, 旨在提醒讀者在不同平臺和解釋器下內存布局和實現上存在差異,但編程思想通用,文中內容大多來自筆者實際工作經驗和網上搜集,力求正確,但水平有限,如有不當之處,敬請指出

面向對象:本文面向有一定C/C++基礎,并且可以讀懂部分匯編的讀者

版權:歡迎轉載,但請注明出處http://m.shnenglu.com/dawnbreak/ 保留對本文的一切權力

目錄
1. C++基本類型與結構體內存布局
                Key words: class,  struct, memory alignment

2.虛表, 多態與動態綁定

                Key words: Virtual Table, polymiorphism

3.對象池

                Key words: object pool , reload, new ,delete

4.內存泄漏檢測

                Key words: memory leak detect

5.智能指針

                Key words: smart pointer

6.   編譯期類型約束
   
                Key words: compile-time ,type-constraint


 Appendix 1: C++堆棧祥解



第二章  虛表, 多態與動態綁定

Key words: Virtual Table, polymiorphism

1.虛表-Virtual Table 多態-polymiorphism

       虛函數表由虛函數的地址組成,表中函數地址的順序和它們第一次出現的順序(即在類定義的順序)一致。若有重載的函數,則替換掉基類函數的地址,事實上你可以簡單的將虛表定義理解如下:

        Int* virtualTable[size]//普通的指針數組而已

        
多數情形下,MSVC的類按如下格局分布:

?           指向虛函數表的指針(_vtable__vftable_),不過它只在類包括虛函數,以及不能從基類復用合適的函數表時才會被添加。

?           基類。

?             函數成員。

請看如下例子:


#include "stdafx.h"
#include 
"assert.h"
#include 
"iostream"
using namespace std;
class A
{
public:
  
int b1;
  
static int b3;
  
int b2;
public:

  
virtual void A_virt1()
  
{
      std::cout
<<"  call of first A's vf"<<std::endl;
  }


  
virtual void A_virt2()
  
{
      std::cout
<<"  call of second A's vf"<<std::endl;
  }


}
;
int A::b3=100;
//#pragma pack(1)
class B:public A
{
    
int a1;
    
char b1;
    
float c1;
  
virtual void A_virt2()
  
{
      std::cout
<<"  call of second B's vf"<<std::endl;
  }
    
  
virtual void B_virt1()
  
{
        std::cout
<<"  call of second B's vf1"<<std::endl;
  }

    
virtual void B_virt2()
  
{
        std::cout
<<"  call of second B's vf2"<<std::endl;
  }

    
void getsome();
}
;
void B::getsome()
{
    
int a=1;
}


class D:public A
{
}
;

class C:public B,public D
{
  
virtual void B_virt1()
  
{
        std::cout
<<"  call of first C's vf"<<std::endl;
  }

}
;
int _tmain(int argc, _TCHAR* argv[])
{
    typedef 
void(*pfunc)();
    cout
<<"test the class memory layout-virtual table"<<endl;
    C cc;
    (pfunc(((
int**)(&cc))[0][0]))();
    (pfunc(((
int**)(&cc))[0][1]))();
    (pfunc(((
int**)(&cc))[0][2]))();
    (pfunc(((
int**)(&cc))[0][3]))();

    system(
"pause");
    
return 0;
}

程序輸出結果:


以下是各個類在內存中的布局圖

class A size(12):
 
+---
 
0 | {vfptr}
 
4 | b1
 
8 | b2
 
+---
A::$vftable@:
 
| &A_meta
 
|  0
 
0 | &A::A_virt1
 
1 | &A::A_virt2
A::A_virt1 
this adjustor: 0
A::A_virt2 
this adjustor: 0
class B size(24):
 
+---
 
| +--- (base class A)
 
0 | | {vfptr}
 
4 | | b1
 
8 | | b2
 
| +---
12 | a1
16 | b1
   
| <alignment member> (size=3)
20 | c1
 
+---
B::$vftable@:
 
| &B_meta
 
|  0
 
0 | &A::A_virt1
 
1 | &B::A_virt2
 
2 | &B::B_virt1
 
3 | &B::B_virt2
B::A_virt2 
this adjustor: 0
B::B_virt1 
this adjustor: 0
B::B_virt2 
this adjustor: 0
class D size(12):
 
+---
 
| +--- (base class A)
 
0 | | {vfptr}
 
4 | | b1
 
8 | | b2
 
| +---
 
+---
D::$vftable@:
 
| &D_meta
 
|  0
 
0 | &A::A_virt1
 
1 | &A::A_virt2
class C size(36):
 
+---
 
| +--- (base class B)
 
| | +--- (base class A)
 
0 | | | {vfptr}
 
4 | | | b1
 
8 | | | b2
 
| | +---
12 | | a1
16 | | b1
   
| | <alignment member> (size=3)
20 | | c1
 
| +---
 
| +--- (base class D)
 
| | +--- (base class A)
24 | | | {vfptr}
28 | | | b1
32 | | | b2
 
| | +---
 
| +---
 
+---
C::$vftable@B@:
 
| &C_meta
 
|  0
 
0 | &A::A_virt1
 
1 | &B::A_virt2
 
2 | &C::B_virt1
 
3 | &B::B_virt2
C::$vftable@D@:
 
| -24
 
0 | &A::A_virt1
 
1 | &A::A_virt2
C::B_virt1 
this adjustor: 0

為了調用虛函數,編譯器首先需要從_vftable_取得函數地址,然后就像調用簡單方法一樣(例如,傳入_this_指針作為隱含參數)。例如:

          cc.A_virt2()

          ;esi = ptr [cc]

          mov eax, [esi]  ;fetch virtual table pointer

          mov ecx, esi

          call [eax+4]  ;call second virtual method

          ;cc->B_virt1()

          ;edi = pC

          lea edi, [esi+8] ;adjust this pointer

          mov eax, [edi]   ;fetch virtual table pointer

          mov ecx, edi

call [eax]       ;call first virtual method


注意到上面class A的內存布局圖,首先是VT指針,然后是成員變量b1,b2, 而對于靜態成員b3并沒有體現,事實上b是存儲在程序的全局靜態數據區,供該類的所有實例共享,這里請注意在classA中虛表中虛函數出現的順序和位置,這一點很重要,接著再看classB中虛函數出現的順序和位置,注意到A_virt1,A_virt2在classA和classB中出現的順序和位置一致,而所不同的是在classB的虛表中A_virt2已經被替換,這就是多態的關鍵所在,每一個虛函數本身其實不過是一個固定的偏移量,而真正實現多態的其實是在編譯器的虛函數表的替換動作.

而對于多繼承情況要復雜一些,例如在ClassC中每一個繼承路徑中都存在一個虛表,如果在沒函數里再加入如下調用:
 (pfunc(((int**)(&cc))[6][0]))();
 (pfunc(((int**)(&cc))[6][1]))();
會輸出:
call of first A's vf
call of second A' vf
這樣一個類中同時存在兩個一抹一樣的函數,那么當你用
C cc;
cc.A_virt2()時會怎么樣呢?
你會得到以下錯誤:
error C2385: ambiguous access of 'A_virt2'

解決辦法有兩種:
1. 調用時加入域操作符,例如:
cc.A::A_virt2();
cc.B::A_virt2( );
這種辦法最穩妥也最清晰
2. 使用虛基類
代碼改動如下:

。。。。。。。
class B:virtual public A
。。。。。。。
class D:virtual public A
。。。。。。。
int _tmain(int argc, _TCHAR* argv[])
{
。。。。。。。
 (pfunc(((int**)(&cc))[0][0]))();
 (pfunc(((int**)(&cc))[0][1]))();
 //(pfunc(((int**)(&cc))[0][2]))();
 //(pfunc(((int**)(&cc))[0][3]))();
 //(pfunc(((int**)(&cc))[6][0]))();
 //(pfunc(((int**)(&cc))[6][1]))();
 cc.A::A_virt2();
 cc.A_virt2();
。。。。。。。。
}

內存布局變為:

class C size(36):
 
+---
 
| +--- (base class B)
 
0 | | {vfptr}
 
4 | | {vbptr}
 
8 | | a1
12 | | b1
   
| | <alignment member> (size=3)
16 | | c1
 
| +---
 
| +--- (base class D)
20 | | {vbptr}
 
| +---
 
+---
 
+--- (virtual base A)
24 | {vfptr}
28 | b1
32 | b2
 
+---
C::$vftable@B@:
 
| &C_meta
 
|  0
 
0 | &C::B_virt1
 
1 | &B::B_virt2
C::$vbtable@B@:
 
0 | -4
 
1 | 20 (Cd(B+4)A)
C::$vbtable@D@:
 
0 | 0
 
1 | 4 (Cd(D+0)A)
C::$vftable@A@:
 
| -24
 
0 | &A::A_virt1
 
1 | &thunk: this-=4goto B::A_virt2

 

多了一個vbtable存儲偏移量,第一個元素存儲vbtable與該類的偏移量,第二個元素存儲vbtable與公共基類的偏移量,而且注意到,vftable@A 的第二個虛函數被定向到B:A_virt2
這樣問題解決了,但是你會得到一個警告:
Warning 1 warning C4250: 'C' : inherits 'B::B::A_virt2' via dominance
顯示繼承了 'B::B::A_virt2‘ ,也就是說你在調用
cc.A_virt2時,默認直接去調用B::A_virt2,這可能并不是你所期望的,所以使用時需要慎重


2 . 動態邦定與靜態邦定
邦定是指一個計算機程序自身彼此關聯的過程。按照邦定所進行的階段不同,可分為兩種不同的邦定方法:靜態邦定和動態邦定。
靜態邦定
靜態邦定是指邦定工作出現在編譯連接階段,這種邦定又稱早期邦定,因為這種邦定過程是在程序開始運行之前完成的。
在編譯時所進行的這種邦定又稱靜態束定。在編譯時就解決了程序中的操作調用與執行該操作代碼間的關系,確定這種關系又稱為束定,在編譯時束定又稱靜態束定。
 1class AA
 2{
 3public:
 4   void test()
 5   {
 6      cout<<"I am class AA!"<<endl;
 7   }

 8}
;
 9class BB
10{
11public:
12   void test()
13   {
14      cout<<"I am class BB!"<<endl;
15   }

16}
;
17int _tmain(int argc, _TCHAR* argv[])
18{
19    AA *A=(AA *)(new BB);
20    A->test();
21}

讀者可以想一下以上例子的結果,如果說是I am class BB!

C++沒有你想得那么職能,C++調用函數不過是指針偏移,而一般成員函數代碼是在數據存儲區的共享代碼段,聲明了AA類的指針 A 就已經指定了偏移的起點是類型AA的代碼段起點,這一步就是所謂的動態邦定,而調用->test();只能得到I am class AA!

也許你要說我并沒有實例化AA怎么會有那一段代碼呢,請注意代碼生成和實例化是完全不同的兩個階段,編譯在編譯時發現你調用了AA::test(); 那么就會載入相應的symbol,程序啟動時就會載入相應代碼段。

也許你還要說沒有用繼承的關系,那么你可以自己試驗一下使BB繼承自AA 結果還是一樣的

想要實現想要的結果唯一的方法就是使用虛函數來實現動態邦定;

 

posted on 2009-03-11 14:25 pear_li 閱讀(2075) 評論(4)  編輯 收藏 引用 所屬分類: C++

評論

# re: MSVC++ 對象內存模型深入解析與具體應用 (二) 2009-03-11 15:07 電腦學習步步高

  回復  更多評論    

# re: MSVC++ 對象內存模型深入解析與具體應用 (二) 2009-03-12 12:49 夢在天涯

你可以研究下對象與對象的類型信息怎么聯系起來的,就是當調用dynamic_cast的時候為什么能夠正確的識別對象的類型!

  回復  更多評論    

# re: MSVC++ 對象內存模型深入解析與具體應用 (二) 2009-03-12 12:53 夢在天涯

加油!
  回復  更多評論    

# re: MSVC++ 對象內存模型深入解析與具體應用 (二) 2012-09-07 11:02 劉偉

如果把內存布局畫成圖就更好了,像《Inside the C++ Object Model》那樣。感謝樓主的文章。學習了
  回復  更多評論    
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            国产亚洲美州欧州综合国| 最新精品在线| 一区视频在线播放| 国产视频在线观看一区 | 亚洲一级二级在线| 中文在线资源观看网站视频免费不卡| 亚洲国产精品v| 一区二区电影免费观看| 亚洲欧美中文日韩v在线观看| 亚洲欧美日韩综合国产aⅴ| 久久gogo国模裸体人体| 欧美sm视频| 亚洲精品综合久久中文字幕| 亚洲伊人一本大道中文字幕| 欧美一区午夜精品| 猛干欧美女孩| 国产精品国产三级国产专区53| 国产区精品在线观看| 亚洲第一精品夜夜躁人人爽| 亚洲天堂av图片| 麻豆国产va免费精品高清在线| 亚洲国产成人精品女人久久久| 一区二区精品国产| 久久欧美肥婆一二区| 欧美色道久久88综合亚洲精品| 国产亚洲人成网站在线观看| 日韩视频一区二区| 久久久噜噜噜久久狠狠50岁| 亚洲激情影视| 久久久999成人| 国产精品国产精品国产专区不蜜| 黄色在线成人| 香蕉国产精品偷在线观看不卡| 欧美成人免费播放| 欧美一区二区三区电影在线观看| 91久久在线观看| 国产亚洲aⅴaaaaaa毛片| 亚洲成人在线视频播放| 羞羞色国产精品| 亚洲日本无吗高清不卡| 久久国产黑丝| 国产美女精品视频| 一区二区三区毛片| 亚洲黄色视屏| 蜜臀av在线播放一区二区三区| 国产麻豆日韩| 午夜精品成人在线视频| 亚洲免费大片| 欧美国产欧美亚州国产日韩mv天天看完整| 国产伦理一区| 欧美一区二区三区视频在线观看| 日韩一级大片| 欧美三级日本三级少妇99| 亚洲日本激情| 91久久一区二区| 欧美久久九九| 99热免费精品| 亚洲精品三级| 欧美日韩三级| 亚洲小视频在线| 一本色道久久综合亚洲二区三区| 欧美精品一区二区蜜臀亚洲| 亚洲高清免费| 亚洲国产日韩欧美综合久久| 久久婷婷亚洲| 亚洲人成绝费网站色www| 欧美高清在线精品一区| 欧美777四色影视在线| 最近中文字幕日韩精品 | 欧美激情第六页| 免费91麻豆精品国产自产在线观看| 国产一区视频观看| 久久久最新网址| 快射av在线播放一区| 亚洲春色另类小说| 亚洲精品久久嫩草网站秘色 | 欧美国产成人精品| 99视频在线观看一区三区| 亚洲人精品午夜| 国产精品www.| 久久久久成人精品免费播放动漫| 久久九九免费视频| 亚洲美女诱惑| 午夜视频在线观看一区| 亚洲电影免费在线观看| 亚洲精品乱码久久久久久黑人 | 亚洲天堂av电影| 亚洲欧美日韩精品综合在线观看| 极品少妇一区二区三区精品视频| 久久精品99国产精品日本| 久久福利影视| 亚洲精品免费看| 亚洲综合日韩中文字幕v在线| 国内精品一区二区三区| 亚洲国产黄色| 国产精品自拍小视频| 欧美va亚洲va国产综合| 欧美日韩视频不卡| 久久中文久久字幕| 欧美午夜剧场| 欧美国产日韩视频| 国产美女诱惑一区二区| 亚洲国产高清aⅴ视频| 国产啪精品视频| 99精品热6080yy久久 | 国产午夜精品一区理论片飘花 | 亚洲三级电影全部在线观看高清| 国产精品国产成人国产三级| 蜜臀av一级做a爰片久久 | 新片速递亚洲合集欧美合集| 亚洲精品久久久蜜桃| 午夜亚洲视频| 亚洲小视频在线观看| 欧美凹凸一区二区三区视频| 久久精品国产一区二区三区免费看| 母乳一区在线观看| 久久全球大尺度高清视频| 国产精品久久久久久久久搜平片 | 激情综合色丁香一区二区| 一本色道久久综合亚洲精品按摩| 亚洲高清免费在线| 久久久久久久成人| 久久国内精品视频| 国产精品theporn| 99re热精品| 一本色道久久综合亚洲精品小说 | 亚洲一二三级电影| 欧美精品久久天天躁| 女人天堂亚洲aⅴ在线观看| 国产欧美精品va在线观看| 亚洲视频狠狠| 亚洲欧美在线免费观看| 欧美日韩ab| 亚洲免费av电影| 这里只有精品电影| 欧美视频一区二区三区…| 亚洲精品一区二区三区福利| 亚洲欧洲日本一区二区三区| 亚洲视频一区在线| 欧美成人乱码一区二区三区| 美女脱光内衣内裤视频久久网站| 国产午夜精品一区理论片飘花| 亚洲图片欧洲图片日韩av| 亚洲免费视频一区二区| 欧美色区777第一页| 一本色道久久综合亚洲精品按摩| 在线亚洲一区观看| 国产精品日韩高清| 午夜欧美电影在线观看| 久久精品日产第一区二区三区| 国产日韩欧美综合| 久久久午夜电影| 亚洲黄一区二区| 亚洲特级毛片| 国产日本欧洲亚洲| 久久综合网hezyo| 亚洲精品色图| 欧美一级久久| 亚洲国产经典视频| 欧美日韩国产综合在线| 亚洲无亚洲人成网站77777| 久久国产日韩| 亚洲精品一区二区在线| 国产精品igao视频网网址不卡日韩| 亚洲一二三区在线| 裸体一区二区三区| 这里只有精品电影| 国内精品久久久久久影视8| 美女脱光内衣内裤视频久久网站| 亚洲精品一二| 久久久国际精品| 99re热精品| 黄色综合网站| 国产精品v欧美精品v日韩精品| 久久av在线| 在线一区观看| 亚洲国产免费| 久久伊伊香蕉| 亚洲自拍16p| 亚洲精品欧美精品| 国产精品永久免费| 欧美伦理在线观看| 久久乐国产精品| 亚洲男人的天堂在线| 亚洲日本在线观看| 欧美高清不卡| 久久综合九色九九| 午夜欧美大片免费观看| 99热免费精品在线观看| 亚洲春色另类小说| 激情婷婷久久| 国产在线一区二区三区四区| 国产精品国产亚洲精品看不卡15| 欧美凹凸一区二区三区视频| 欧美中文字幕久久| 午夜欧美视频| 亚洲欧美日韩精品在线| 亚洲香蕉成视频在线观看| 99在线热播精品免费| 日韩视频在线观看免费|