轉載自http://blog.csdn.net/qq_35524916/article/details/69367213
我們知道在如下例子中。
class Test
{
public:
Fun(int a)
{
//doSomething
}
private:
int a;
};
Fun(int a)是Test的非靜態成員函數,所以編譯器在編譯期間改寫該函數時,會自動將一個名為this的指針當作為第一個參數傳入。
類似于:
Fun(Test* const this,int a);
那么問題來了,我們理解這個this指針呢?它有哪些用法和限制呢?
本文試圖通過如下幾個模塊循環漸進的解析this指針,爭取達到深入淺出的效果。
this指針是什么?
this指針的六大屬性。
this指針的一些用法。
this指針的注意事項。
-
this指針是什么?
關于this指針超級經典的一段話:
當你進入一個房子后,
你可以看見桌子、椅子、地板等,
但是房子你是看不到全貌了。
對于一個類的實例來說,
你可以看到它的成員函數、成員變量,
但是實例本身呢?
this是一個指針,它時時刻刻指向你這個實例本身。
-
this指針的六大屬性
我一直都認為若想對某一變量有個準確的認知,就必須了解其六大屬性。
這些屬性是我們對于this指針應該以何種方式正確使用的基礎。
(1)名稱屬性:標識符this表示。
(2)類型屬性:類類型* const(類似于類引用的類型)
(3)值屬性:表示當前調用該函數對象的首地址。
(4)作用域:this指針是編譯器默認傳給類中非靜態函數的隱含形參,所以其作用域在非靜態成員函數的函數體內。
(5)鏈接屬性:在該類作用域中,不同類的非靜態成員函數中,this這個指針變量的鏈接屬性是內部的,但其所指對象是外部的,即this變量是不同的實體,但指向對象是同一個。
(6)存儲類型:this指針是由編譯器生成,當類的非靜態成員函數的參數個數一定時,this指針存儲在ecx寄存器中;若該函數參數個數未定(可變參數函數),則存放在棧中。
-
this指針的一些用法。
(1)作為當前類的指針,用來區別形參和成員變量名稱相同的情況。
比如本文開頭的例子,在Test::Test(int a)函數的作用域中,形參a與類的成員變量a同名,則形參a會將成員變量a隱藏。
我們無法直接訪問成員變量a,除了在a前面加上類的名字如Test::a來表示類Test的成員變量a外,
還可以顯示使用this指針來強制訪問對象成員this->a
(2)this作為返回值,返回當前對象的引用。
在賦值運算符重載函數中,為了不減少賦值運算符的功能,我們需要返回一個引用,來支持重復賦值(例如a = b =c;)
同樣,在類的賦值運算符重載函數中,我們可以一般使用(*this)作為當前對象返回。
-
this指針的注意事項。
(1)this指針不能為空。
因為this指向一個已存在的類的實例對象,可以通過"->"的方式,訪問類中成員,左移this為空是非法的。
例如,在本文開頭例子中,將成員變量a的訪問權限改寫為public,然后執行下面代碼,雖然編譯會通過,但程序運行會崩潰。
Test* pT = NULL;
pT->a = 10;
(2)為什么使用的是指針,而不是引用。
this指針的類型和類引用的類型都是Test* const但是,由于歷史原因,C++最初是沒有引用,而有指針的,在設計類時,就采用了指針的方式,就導致了現在的局面。雖然引用理論上也可以實現this的功能。
(3)this指針并不是對象的一部分,不影響sizeof的結果。
(4)this指針是由編譯器自動生成,作為函數的第一個參數,用戶不能顯示傳遞。
(5)this指針不能在構造函數的初始化列表中給對象的成員變量賦值,
初始化列表本義是在創建類的實例對象時,給其中成員變量賦初值。即此時對象還未創建完畢。而this指針是類在創建之后,由編譯器自動生成的指針,指向對象的首地址。簡單來說,先有對象,后有this指針。所以this指針不能在初始化列表中給成員指針賦初值。
(6)關于this的存儲及傳參順序
當類的非靜態成員函數的參數個數是一定時,this指針存儲在ecx寄存器上,通過ecx傳遞給調用者,此時函數調用約定是_thiscall。若參數個數不確定(可變參數)時,則借助棧,在所有的參數被壓棧后,再壓入棧中,此時函數調用約定是_cdecl。
示例:將Test類簡化,只聲明,不定義。(可以方便的觀察函數的調用約定和函數名的改寫)
class Test
{
public:
void Fun(int a);
};
其中,Fun(int a)的個數確定,只有一個。則鏈接時會報錯:
error LNK2019: 無法解析的外部符號 "public: void __thiscall Test::Fun(int)" (?Fun@Test@@QAEXH@Z),該符號在函數 _main 中被引用
將void Fun(int a);改為void Fun(...);則鏈接時報錯為:
error LNK2019: 無法解析的外部符號 "public: void __cdecl Test::Fun(...)" (?Fun@Test@@QAAXZZ),該符號在函數 _main 中被引用
可以證明,當函數參數不同時,函數的調用約定不同,this的存儲類型不同,造成了其傳參順序的不同。