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

兔子的技術博客

兔子

   :: 首頁 :: 聯系 :: 聚合  :: 管理
  202 Posts :: 0 Stories :: 43 Comments :: 0 Trackbacks

留言簿(10)

最新評論

閱讀排行榜

評論排行榜

撰文:Don Clugston

翻譯:周翔

(接上篇)

成員函數指針——為什么那么復雜?

類的成員函數和標準的C函數有一些不同。與被顯式聲明的參數相似,類的成員函數有一個隱藏的參數this,它指向一個類的實例。根據不同的編譯器,this或者被看作內部的一個正常的參數,或者會被特別對待(比如,在VC++中,this一般通過ECX寄存器來傳遞,而普通的成員函數的參數被直接壓在堆棧中)。this作為參數和其他普通的參數有著本質的不同,即使一個成員函數受一個普通函數的支配,在標準C++中也沒有理由使這個成員函數和其他的普通函數(ordinary function)的行為相同,因為沒有thiscall關鍵字來保證它使用像普通參數一樣正常的調用規則。成員函數是一回事,普通函數是另外一回事(Member functions are from Mars, ordinary functions are from Venus)。

你可能會猜測,一個成員函數指針和一個普通函數指針一樣,只是一個代碼指針。然而這種猜測也許是錯誤的。在大多數編譯器中,一個成員函數指針要比一個普通的函數指針要大許多。更奇怪的是,在Visual C++中,一個成員函數指針可以是4812甚至16個字節長,這取決于它所相關的類的性質,同時也取決于編譯器使用了怎樣的編譯設置!成員函數指針比你想象中的要復雜得多,但也不總是這樣。

讓我們回到二十世紀80年代初期,那時,最古老的C++編譯器CFront剛剛開發完成,那時C++語言只能實現單一繼承,而且成員函數指針剛被引入,它們很簡單:它們就像普通的函數指針,只是附加了額外的this作為它們的第一個參數,你可以將一個成員函數指針轉化成一個普通的函數指針,并使你能夠對這個額外添加的參數產生足夠的重視。

這個田園般的世界隨著CFront 2.0的問世被擊得粉碎。它引入了模版和多重繼承,多重繼承所帶來的破壞造成了成員函數指針的改變。問題在于,隨著多重繼承,調用之前你不知道使用哪一個父類的this指針,比如,你有4個類定義如下:

class A {

public:

virtual int Afunc() { return 2; };

};

class B {

public:

int Bfunc() { return 3; };

};

// C是個單一繼承類,它只繼承于A

class C: public A {

public:

int Cfunc() { return 4; };

};

// D 類使用了多重繼承

class D: public A, public B {

public:

int Dfunc() { return 5; };

};

假如我們建立了C類的一個成員函數指針。在這個例子中,AfuncCfunc都是C的成員函數,所以我們的成員函數指針可以指向Afunc或者Cfunc。但是Afunc需要一個this指針指向C::A(后面我叫它Athis),而Cfunc需要一個this指針指向C(后面我叫它Cthis)。編譯器的設計者們為了處理這種情況使用了一個把戲(trick):他們保證了A類在物理上保存在C類的頭部(即C類的起始地址也就是一個A類的一個實例的起始地址),這意味著Athis == Cthis。我們只需擔心一個this指針就夠了,并且對于目前這種情況,所有的問題處理得還可以。

現在,假如我們建立一個D類的成員函數指針。在這種情況下,我們的成員函數指針可以指向AfuncBfuncDfunc。但是Afunc需要一個this指針指向D::A,而Bfunc需要一個this指針指向D::B。這時,這個把戲就不管用了,我們不可以把A類和B類都放在D類的頭部。所以,D類的一個成員函數指針不僅要說明要指明調用的是哪一個函數,還要指明使用哪一個this指針。編譯器知道A類占用的空間有多大,所以它可以對Athis增加一個delta = sizeof(A)偏移量就可以將Athis指針轉換為Bthis指針。

如果你使用虛擬繼承(virtual inheritance),比如虛基類,情況會變得更糟,你可以不必為搞懂這是為什么太傷腦筋。就舉個例子來說吧,編譯器使用虛擬函數表virtual function table——“vtable”)來保存每一個虛函數、函數的地址和virtual_delta將當前的this指針轉換為實際函數需要的this指針時所要增加的位移量。

綜上所述,為了支持一般形式的成員函數指針,你需要至少三條信息:函數的地址,需要增加到this指針上的delta位移量,和一個虛擬函數表中的索引。對于MSVC來說,你需要第四條信息:虛擬函數表(vtable)的地址。

成員函數指針的實現

那么,編譯器是怎樣實現成員函數指針的呢?這里是對不同的326416位的編譯器,對各種不同的數據類型(有intvoid*數據指針、代碼指針(比如指向靜態函數的指針)、在單一(single-)繼承、多重(multiple-)繼承、虛擬(virtual-)繼承和未知類型(unknown)的繼承下的類的成員函數指針)使用sizeof運算符計算所獲得的數據:

編譯器

選項

int

DataPtr

CodePtr

Single

Multi

Virtual

Unknown

MSVC

4

4

4

4

8

12

16

MSVC

/vmg

4

4

4

16#

16#

16#

16

MSVC

/vmg /vmm

4

4

4

8#

8#

--

8#

Intel_IA32

4

4

4

4

8

12

12

Intel_IA32

/vmg /vmm

4

4

4

4

8

--

8

Intel_Itanium

4

8

8

8

12

20

20

G++

4

4

4

8

8

8

8

Comeau

4

4

4

8

8

8

8

DMC

4

4

4

4

4

4

4

BCC32

4

4

4

12

12

12

12

BCC32

/Vmd

4

4

4

4

8

12

12

WCL386

4

4

4

12

12

12

12

CodeWarrior

4

4

4

12

12

12

12

XLC

4

8

8

20

20

20

20

DMC

small

2

2

2

2

2

2

2

DMC

medium

2

2

4

4

4

4

4

WCL

small

2

2

2

6

6

6

6

WCL

compact

2

4

2

6

6

6

6

WCL

medium

2

2

4

8

8

8

8

WCL

large

2

4

4

8

8

8

8

注:

# 表示使用__single/__multi/__virtual_inheritance關鍵字的時候代表4812

這些編譯器是Microsoft Visual C++ 4.0 to 7.1 (.NET 2003), GNU G++ 3.2 (MingW binaries, http://www.mingw.org/), Borland BCB 5.1 (http://www.borland.com/), Open Watcom (WCL) 1.2 (http://www.openwatcom.org/), Digital Mars (DMC) 8.38n (http://www.digitalmars.com/), Intel C++ 8.0 for Windows IA-32, Intel C++ 8.0 for Itanium, (http://www.intel.com/), IBM XLC for AIX (Power, PowerPC), Metrowerks Code Warrior 9.1 for Windows (http://www.metrowerks.com/),  Comeau C++ 4.3 (http://www.comeaucomputing.com/). Comeau的數據是在它支持的32位平臺(x86, Alpha, SPARC等)上得出的。16位的編譯器的數據在四種DOS配置(tiny, compact, medium,  large)下測試得出,用來顯示各種不同代碼和數據指針的大小。MSVC/vmg的選項下進行了測試,用來顯示“成員指針的全部特性”。(如果你擁有在列表中沒有出現的編譯器,請告知我。非x86處理機下的編譯器測試結果有獨特的價值。)

 

看著表中的數據,你是不是覺得很驚奇?你可以清楚地看到編寫一段在一些環境中可以運行而在另一些編譯器中不能運行的代碼是很容易的。不同的編譯器之間,它們的內部實現顯然是有很大差別的;事實上,我認為編譯器在實現語言的其他特性上并沒有這樣明顯的差別。對實現的細節進行研究你會發現一些奇怪的問題。

一般,編譯器采取最差的,而且一直使用最普通的形式。比如對于下面這個結構:

// Borland (缺省設置Watcom C++.

struct {

FunctionPointer m_func_address;

int m_delta;

int m_vtable_index; //如果不是虛擬繼承,這個值為0

};

// Metrowerks CodeWarrior使用了稍微有些不同的方式。

//即使在不允許多重繼承的Embedded C++的模式下,它也使用這樣的結構!

struct {

int m_delta;

int m_vtable_index; // 如果不是虛擬繼承,這個值為-1

FunctionPointer m_func_address;

};

// 一個早期的SunCC版本顯然使用了另一種規則:

struct {

int m_vtable_index; //如果是一個非虛擬函數(non-virtual function),這個值為0

FunctionPointer m_func_address; //如果是一個虛擬函數(virtual function),這個值為0

int m_delta;

};

//下面是微軟的編譯器在未知繼承類型的情況下或者使用/vmg選項時使用的方法:

struct {

FunctionPointer m_func_address;

int m_delta;

int m_vtordisp;

int m_vtable_index; // 如果不是虛擬繼承,這個值為0

};

// AIX (PowerPC)IBMXLC編譯器:

struct {

FunctionPointer m_func_address; // PowerPC來說是64

int m_vtable_index;

int m_delta;

int m_vtordisp;

};

// GNU g++使用了一個機靈的方法來進行空間優化

struct {

union {

FunctionPointer m_func_address; // 其值總是4的倍數

int m_vtable_index_2; // 其值被2除的結果總是奇數

};

int m_delta;

};

對于幾乎所有的編譯器,deltavindex用來調整傳遞給函數的this指針,比如Borland的計算方法是:

adjustedthis = *(this + vindex -1) + delta // 如果vindex!=0

adjustedthis = this + delta // 如果vindex=0

(其中,“*”是提取該地址中的數值,adjustedthis是調整后的this指針——譯者注)

Borland使用了一個優化方法:如果這個類是單一繼承的,編譯器就會知道delta和vindex的值是0,所以它就可以跳過上面的計算方法。

GNU編譯器使用了一個奇怪的優化方法。可以清楚地看到,對于多重繼承來說,你必須查看vtable(虛擬函數表)以獲得voffset(虛擬函數偏移地址)來計算this指針。當你做這些事情的時候,你可能也把函數指針保存在vtable中。通過這些工作,編譯器將m_func_addressm_vtable_index合二為一(即放在一個union中),編譯器區別這兩個變量的方法是使函數指針(m_func_address)的值除以2后結果為偶數,而虛擬函數表索引(m_vtable_index_2)除以2后結果為奇數。它們的計算方法是:

adjustedthis = this + delta

if (funcadr & 1) //如果是奇數

call (* ( *delta + (vindex+1)/2) + 4)

else //如果是偶數

call funcadr

(其中, funcadr是函數地址除以2得出的結果。——譯者注)

InterItanium編譯器(但不是它們的x86編譯器)對虛擬繼承(virtual inheritance)的情況也使用了unknown_inheritance結構,所以,一個虛擬繼承的指針有20字節大小,而不是想象中的16字節。

// Itaniumunknown  virtual inheritance下的情況.

struct {

FunctionPointer m_func_address; //Itanium來說是64

int m_delta;

int m_vtable_index;

int m_vtordisp;

};

我不能保證Comeau C++使用的是和GNU相同的技術,也不能保證它們是否使用short代替int使這種虛擬函數指針的結構的大小縮小至8個字節。最近發布的Comeau C++版本為了兼容微軟的編譯器也使用了微軟的編譯器關鍵字(我想它也只是忽略這些關鍵字而不對它們進行實質的相關處理罷了)。

Digital Mars編譯器(即最初的Zortech C++到后來的Symantec C++)使用了一種不同的優化方法。對單一繼承類來說,一個成員函數指針僅僅是這個函數的地址。但涉及到更復雜的繼承時,這個成員函數指針指向一個形式轉換函數(thunk function,這個函數可以實現對this指針的必要調整并可用來調用實際的成員函數。每當涉及到多重繼承的時候,每一個成員函數的指針都會有這樣一個形式轉換函數,這對函數調用來說是非常有效的。但是這意味著,當使用多重繼承的時候,子類的成員函數指針向基類成員函數指針的轉換就會不起作用了。可見,這種編譯器對編譯代碼的要求比其他的編譯器要嚴格得多。

很多嵌入式系統的編譯器不允許多重繼承。這樣,這些編譯器就避免了可能出現的問題:一個成員函數指針就是一個帶有隱藏this指針參數的普通函數指針。

微軟"smallest for class"方法的問題

微軟的編譯器使用了和Borland相似的優化方法。它們都使單一繼承的情況具有最優的效率。但不像Borland,微軟在缺省條件下成員函數指針省略了值為的指針入口(entry),我稱這種技術為“smallest for class”方法:對單一繼承類來說,一個成員函數指針僅保存了函數的地址(m_func_address),所以它有4字節長。而對于多重繼承類來說,由于用到了偏移地址(m_delta),所以它有8字節長。對虛擬繼承,會用到12個字節。這種方法確實節省空間,但也有其它的問題。

首先,將一個成員函數指針在子類和基類之間進行轉化會改變指針的大小!因此,信息是會丟失的。其次,當一個成員函數指針在它的類定義之前聲明的時候,編譯器必須算出要分配給這個指針多少空間,但是這樣做是不安全的,因為在定義之前編譯器不可能知道這個類的繼承方式。對Intel C++和早期的微軟編譯器來說,編譯器僅僅對指針的大小進行猜測,一旦在源文件中猜測錯誤,你的程序會在運行時莫名其妙地崩潰。所以,微軟的編譯器中增加了一些保留字:__single_inheritance, __multiple_inheritance, __virtual_inheritance,并增設了一些編譯器開關(compiler switch),如/vmg,讓所有的成員函數指針有相同的大小,而對原本個頭小的成員函數指針的空余部分用0填充。Borland編譯器也增加了一些編譯器開關,但沒有增加新的關鍵字。Intel的編譯器可以識別Microsoft增加的那些關鍵字,但它在能夠找到類的定義的情況下會對這些關鍵字不做處理。

對于MSVC來說,編譯器需要知道類的vtable在哪兒;通常就會有一個this指針的偏移量(vtordisp),這個值對所有這個類中的成員函數來說是不變的,但對每個類來說會是不同的。對于MSVC,經調整過的this指針是在原this指針的基礎上經過下面的計算得出的:

if (vindex=0) //如果不是虛擬繼承(_virtual_inheritance

adjustedthis = this + delta

else //如果是

adjustedthis = this + delta + vtordisp + *(*(this + vtordisp) + vindex)

在虛擬繼承的情況下,vtordisp的值并不保存在__virtual_inheritance指針中,而是在發現函數調用的代碼時,編譯器才將其相應的匯編代碼“嵌”進去。但是對于未知類型的繼承,編譯器需要盡可能地通過讀代碼確定它的繼承類型,所以,編譯器將虛擬繼承指針(virtual inheritance pointer)分為兩類(__virtual_inheritance__unknown_inheritance)。

理論上,所有的編譯器設計者應該在MFP(成員函數指針)的實現上有所變革和突破。但在實際上,這是行不通的,因為這使現在編寫的大量代碼都需要改變。微軟曾發表了一篇非常古老的文章(http://msdn.microsoft.com/archive/en-us/dnarvc/html/jangrayhood.asp)來解釋Visual C++運作的實現細節。這篇文章是Jan Gray寫的,他曾在1990年設計了Microsoft C++的對象模型。盡管這篇文章發表于1994年,但這篇文章仍然很重要——這意味著C++的對象模型在長達15年的時間里(1990年到2004年)沒有絲毫改變。

現在,我想你對成員函數指針的事情已經知道得太多了。要點是什么?我已為你建立了一個規則。雖然各種編譯器的在這方面的實現方法有很大的不同,但是也有一些有用的共同點:不管對哪種形式的類,調用一個成員函數指針生成的匯編語言代碼是完全相同的。有一種特例是使用了“smallest for class”技術的非標準的編譯器,即使是這種情況,差別也是很微小的。這個事實可以讓我們繼續探索怎樣去建立高性能的委托(delegate)。

(待續)


轉自:http://blog.csdn.net/zhoulingj/archive/2004/07/23/49799.aspx

posted on 2010-12-15 09:21 會飛的兔子 閱讀(442) 評論(0)  編輯 收藏 引用 所屬分類: C++及開發環境
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            99视频热这里只有精品免费| 欧美一区二区三区免费在线看| 欧美ab在线视频| 久久成人免费电影| 午夜欧美大尺度福利影院在线看| 中文网丁香综合网| 正在播放日韩| 午夜精品久久久久| 亚洲男人的天堂在线aⅴ视频| 一区二区三区欧美日韩| 在线一区亚洲| 亚洲图片在区色| 在线视频精品一区| 99亚洲一区二区| 亚洲图色在线| 欧美一区二区三区在线| 久久久久国产一区二区三区四区 | 久久久综合视频| 玖玖视频精品| 欧美日韩小视频| 欧美日韩中文| 国产一级揄自揄精品视频| 国内伊人久久久久久网站视频| 精品成人一区二区三区| 亚洲精品视频在线看| 很黄很黄激情成人| 亚洲精美视频| 午夜精品区一区二区三| 久久久www免费人成黑人精品| 亚洲成在线观看| 亚洲精品一区在线| 久久动漫亚洲| 欧美色精品天天在线观看视频| 国产精品视频观看| 在线观看国产一区二区| 亚洲一区二区三区在线观看视频| 久久久久在线| 亚洲精品中文字幕在线| 欧美在线播放一区二区| 欧美人成在线| 亚洲成色最大综合在线| 午夜亚洲伦理| 日韩图片一区| 欧美sm极限捆绑bd| 好看的日韩视频| 欧美一区国产二区| 一级日韩一区在线观看| 欧美wwwwww| 黄色影院成人| 久久精品二区三区| 亚洲一区二区三区三| 欧美大片免费观看| 玉米视频成人免费看| 欧美亚洲在线观看| 国产精品99久久久久久人| 欧美激情五月| 亚洲欧洲一区二区在线观看| 久久日韩精品| 欧美在线黄色| 国产一区二区三区在线观看网站| 午夜国产精品视频免费体验区| 91久久久亚洲精品| 久久精品国产免费观看| 国产日韩亚洲欧美综合| 香蕉免费一区二区三区在线观看 | 亚洲免费成人| 欧美另类69精品久久久久9999| 蜜桃av噜噜一区二区三区| 亚洲国产精品成人一区二区| 午夜精品av| 亚洲小说欧美另类社区| 欧美午夜免费影院| 亚洲欧美国产77777| 亚洲视频在线视频| 国产精品日韩欧美一区二区三区 | 午夜精品美女自拍福到在线| 亚洲午夜精品久久久久久浪潮| 欧美性色视频在线| 欧美夜福利tv在线| 久久国产毛片| 亚洲国产一区二区三区高清| 亚洲国产日韩在线| 欧美女同在线视频| 亚洲欧美日韩另类| 欧美一区二区三区免费大片| 韩国一区电影| 欧美黄色一区| 欧美日韩亚洲高清| 欧美一区二区三区免费在线看| 欧美在线视频导航| 亚洲国产精品t66y| 亚洲人精品午夜| 国产精品美女久久久浪潮软件 | 久久午夜色播影院免费高清| 老司机午夜免费精品视频| 亚洲伦理久久| 亚洲欧美日韩另类| 亚洲第一偷拍| 国产精品99久久久久久www| 狠狠色狠狠色综合人人| 亚洲免费成人av电影| 好吊妞**欧美| 日韩亚洲视频| 樱桃成人精品视频在线播放| 亚洲人成高清| 狠狠色狠狠色综合日日小说| 最新热久久免费视频| 国产欧美日韩高清| 日韩视频精品| 亚洲国产精品一区二区www在线 | 亚洲欧美日本国产有色| 亚洲激情影院| 久久国产精品久久国产精品 | 亚洲福利视频专区| 国产亚洲欧美日韩在线一区| 亚洲乱码国产乱码精品精天堂| 黄色日韩在线| 亚洲欧美精品中文字幕在线| 一本一本久久a久久精品综合妖精| 久久狠狠婷婷| 欧美黄色免费| 一区在线视频观看| 一区二区欧美亚洲| 亚洲精品久久久久久久久久久| 亚洲欧美在线播放| 亚洲一区二区三区在线播放| 欧美国产日韩免费| 久久综合九色99| 国产午夜精品视频免费不卡69堂| 91久久精品一区二区别| 亚洲精品老司机| 在线欧美亚洲| 久久精品国产v日韩v亚洲| 在线视频欧美一区| 中日韩男男gay无套| 欧美一区=区| 亚洲人成艺术| 久久亚洲欧美| 午夜精品久久久久久久99水蜜桃| 欧美一区午夜精品| 午夜精品视频网站| 欧美日韩精品一区二区三区四区 | 欧美国产精品久久| 久久免费视频在线| 国产精品久久久91| 亚洲日本电影在线| 日韩午夜三级在线| 你懂的国产精品| 免费在线欧美黄色| 伊人久久成人| 久久香蕉国产线看观看av| 久久久国产精品亚洲一区 | 亚洲一区二区三区四区在线观看 | 国产精品九色蝌蚪自拍| 亚洲三级性片| 亚洲精品在线视频| 欧美一级大片在线免费观看| 久久精品在线播放| 国产伦一区二区三区色一情| 亚洲一级片在线观看| 亚洲综合日韩在线| 国产精品区一区| 亚洲欧洲一区二区在线播放| 欧美激情精品久久久久久免费印度| 欧美福利视频一区| 亚洲成色999久久网站| 米奇777超碰欧美日韩亚洲| 蜜臀va亚洲va欧美va天堂| 欧美日韩免费看| 午夜精品在线观看| 久久久久看片| 亚洲二区免费| 欧美激情一区二区在线| 亚洲男人天堂2024| 欧美中文字幕精品| 99精品国产福利在线观看免费| 欧美www视频| 亚洲视频一区| 免费成人在线观看视频| 亚洲高清在线播放| 亚洲视频欧美视频| 欧美成人精品不卡视频在线观看| 亚洲午夜女主播在线直播| 久久精品99国产精品| 亚洲第一狼人社区| 欧美日韩国产三级| 久久精品夜色噜噜亚洲a∨| 欧美国产日产韩国视频| 正在播放亚洲| 国内精品伊人久久久久av影院| 性做久久久久久免费观看欧美| 91久久黄色| 欧美一区二区三区四区在线观看| 黄色精品在线看| 欧美午夜精品理论片a级大开眼界| 噜噜噜久久亚洲精品国产品小说| 日韩一级二级三级| 久久看片网站| 中文久久精品| 日韩一本二本av|