this指標探秘

2021-04-15 01:47:00 字數 3155 閱讀 8745

參考**:

深度探索c++物件模型對this的描述是,this是乙個函式引數

float   manitude3d(const   point3d   *_this)

float   point3d::manitude3d()const

這兩種方式是等價的,編譯器在內部將後者轉化為前者,因此

obj.magintude();變成了maginitude_7point3dfv(&obj);

如何理解「編譯器在內部將後者轉化為前者」這一句話呢?我們知道世界上第乙個c++編譯器是cfront,bjarne stroustrup,lippman等人開發的,那在當時最流行的程式語言是c,在各種機器各種作業系統下都有c的編譯器版本,這樣c的程式在當時是最易移植的,因此bjarne stroustrup決定cfront生成等價的c**以獲得可移植性。就是說cfront讀入你編寫的.cpp檔案經過詞法分析等編譯過程,結果沒有生成.obj檔案卻生成了乙個.c檔案,然後再用c編譯器對.c檔案進行編譯鏈結,最終生成的程式是乙個徹頭徹尾的c程式(所以cfront也被稱為c 預處理器)

這樣,編譯器在看到成員函式的定義時會直接修改源**:修改函式名稱,在函式的引數裡新增這麼乙個名叫「this」的指標定義,對成員函式的呼叫也採取類似的方式,在呼叫語句的引數列表偷偷加上物件的位址,最後儲存為.c檔案,由c編譯器進行最終的編譯鏈結,生成可執行程式,可以看到經過編譯器編譯之後,c++的成員函式其實也就是乙個普通的函式,this和element pop(sqstack *s)中的s也沒有什麼本質上的區別,就是乙個指向資料結構的指標而已,今天的c++編譯器已經不再是c預處理器,但是依舊沿用這樣的思想

ok,說到這裡大家對什麼是this指標已經有個感性的理解了吧

第一,this指標為什麼沒有定義就能直接使用,答案是編譯器定義了

第二,this指標是乙個函式引數,所以它的使用範圍僅在成員函式內部

第三,this指標為什麼指向物件?呼叫時偷偷把物件位址給它傳遞過去了

下面我們將進入彙編層次去看一看成員函式的呼叫以及this指標到底是如何傳遞的,恩,需要我們有一點彙編知識,以及通過內嵌的彙編來測試我們的想法

x.foo();                                 //vc6.0的反彙編**

lea   ecx,dword   ptr[ebp-4]             //物件位址儲存到ecx中

call   @ilt   +10(x::foo)(0040100f)   //轉到0040100f處

0040100f   jmp   x::foo               // jmp跳轉的函式真正的位址

void   x::foo()

由此可見,在vc debug中物件位址是通過暫存器變數(ecx)傳遞進成員函式,進入函式後做完一些初始化的工作以後,把物件位址寫入到foo的ebp-4處,程式如果要讀取物件屬性都需要從ebp-4中讀出物件位址,了解vc成員函式的呼叫過程,我們可以大膽做一些測試程式

void   x::foo(x *p)

this- >a=10;//p- >a=10;

}//偷梁換柱,this實際已經指向*p ,這時通過this對物件的修改都是針對*p

void   x::foo()

(*pthis)- >a=10;//this- >a=10;      

}//pthis指向了this因此**pthis==*this

剛才我們是基於vc debug下的乙個討論,現在討論下release下的成員函式呼叫,在release下,物件位址不再寫入ebp-4處,從而使this指標變成乙個純粹的暫存器變數,由於release的彙編**非常難讀,我們只寫乙個測試程式證明我們的觀點:)

void x::foo()

this->a=2;

}因此在this指標在vc中其實是乙個徹底的暫存器的變數,vc的成員函式其實是void  x::foo  ( const  register  x  *this)

ok,上面說了那麼多就是在證明一點:this指標只是乙個普通的指標,被編譯器定義的乙個函式引數,現在,我們要證明另一點,c++的成員函式被編譯之後只是普通的函式,由於vc++的this指標是暫存器變數,不能很清楚的顯示這個關係,我們選用borland c++ builder 6.0 作為我們測試的工具,bcb依靠棧來傳遞物件位址

x.foo();

lea   eax,[ebp-0x04]

push   eax               //將物件位址壓棧

call   x::foo()           //呼叫x::foo()

先介紹一點c++物件模型的知識,我們知道c++的類定義包含虛函式,那麼這個類產生的物件都會包含乙個虛指標,虛指標會指向乙個名叫虛**的函式指標陣列,陣列儲存的都是虛函式的位址,c++依靠這些來實現多型,而通常虛指標又位於物件的前端(我們很幸運,bcb正是這樣)

既然函式存在與記憶體中,那麼我們可以依靠虛指標和虛**的指向關係一路找到虛函式,我們再定義乙個函式指標指向虛函式,通過函式指標呼叫虛函式(並把物件位址傳遞進去)

#pragma   hdrstop

#include  

class   a

virtual   void   bar()

int   x;

}; void   (*pvfunc)   (a*)   ;

int   main()

(*pvfunc)(&a);你可以看到,經過一些非常規的方法我們居然就可以這樣呼叫虛函式,而且我們確實把物件位址給送入到函式裡,可以證實有那麼乙個引數存在,可以證實成員函式就是乙個普通函式。

《c++設計與演化》《深入探索c++物件模型》《c++程式語言》

指標修飾C語言const修飾符探秘指標修飾

ps 今天上午,非常鬱悶,有很多簡單基礎的問題搞得我有些迷茫,哎,幾天不寫就忘。目前又不當coo,還是得用心記 哦!c言語是我接觸的第一門序程設計言語,時當還很傻很無邪,後來敏捷被各種高階言語 但是不得不說,c的位置真的無可撼動。const修飾符在c言語中很用常,但是近最讀 的時候常常搞不清楚,索搜...

ArrayList底層探秘

初始容量 private static final int default capacity 10 空陣列 private static final object empty elementdata 預設容量 private static final object defaultcapacity e...

限流演算法探秘

在資源有限的情況下,遇到突發流量 如雙十一 或系統 rt 劇增,為了保證系統不被拖垮引起更大規模的雪崩,必須進行限流。也就是說限流是系統的自我保護。限流本質上是根據系統處理能力,限制單位時間內處理的請求數量。這裡的視窗是乙個時間視窗,比如把一分鐘劃分為 6 個視窗,則每個視窗的時間範圍是 10 秒。...