從匯編角度,清晰的去看構造函數和this指針到底是個什么東西呢?也許可以解決你的一點小疑問
首先寫一個很簡單的代碼demo:
class A{
public:int a;A(){;}void seta(int _a){a=_a;}A* getA(){return this;}
};int fun1(int px){return px;
}int main(){A aa;aa.seta(8); aa.getA();
}
mov rbp, rsp
:設置新的基址指針,指向當前棧幀。
mov QWORD PTR [rbp-8], rdi
:將 this
指針(存儲在 rdi
中)保存到棧上。
在seta中的部分匯編:
mov rax, QWORD PTR [rbp-8]
:將 this
指針的值加載到 rax
中。
mov DWORD PTR [rax], edx
:將 edx
(即 _a
的值)存儲到 this->a
中。
我們從類成員函數和構造函數中都能看到 兩句關于參數的匯編,同理普通函數中px參數也有著相同的匯編,那么很顯然,對于cpu而言,this指針僅僅是一個參數而已。C++語法糖對于this做了隱藏處理,因此我們在使用的時候才會無感。
而這個有這個this往往可以認為函數的完整使用是這樣:
A aa;aa.seta(8); ====A::seta(&aa,8) 當然這么調用是錯誤的,看著很像類靜態函數
this
指針的創建
- 函數調用前創建:當成員函數被調用時,
this
指針在調用成員函數之前被設置為當前對象的地址。也就是說,在成員函數執行之前,this
指針已經被創建并指向調用該函數的對象。
this
指針的銷毀
- 函數調用結束后銷毀:在成員函數執行完畢并返回后,
this
指針的生命周期就結束了。因為this
只是一個指向對象的指針,當函數返回時,不再需要這個指針,函數調用上下文會自動清理這個隱式參數。
?
學習C++的友友大概率會知道這么一句話:靜態成員函數不屬于類的某個具體對象,而是屬于整個類。這意味著靜態成員函數不能訪問非靜態成員變量和成員函數,因為它們沒有 this
指針。針對這句話我們也可以通過cpu視角進行觀察
而this
指針的存在條件
- 對象實例調用:只有當對象實例調用非靜態成員函數時,
this
指針才會被傳遞給該函數。 - 隱式參數:在非靜態成員函數內部,編譯器會自動添加一個隱式的
this
指針參數,用于引用調用該函數的對象
觀察上圖,你就會看到 靜態成員函數中沒有this指針,即也就沒法去調用相關的成員函數和數據,如果我們想要通過靜態成員函數去調用對象相關的數據,可以通過黃色框框的這種寫法。你會發現這種寫法的匯編同成員函數的匯編相同,也就是說this存的就是傳遞進去的這個對象的地址。
也就有了成員函數的完整版應該是:void setp(A* this,int p);是不是看著參數和示例第二個靜態成員函數一樣~
cpu眼中構造函數與普通函數沒有任何區別:
通過上面的匯編也能看明白,構造含有this指針,且對于cpu來說他與普通函數是相同的
派生類構造函數總會調用基類的構造函數
mov QWORD PTR [rbp-8], rdi
:將this
指針(在rdi
中)保存到局部變量中。mov rax, QWORD PTR [rbp-8]
:將this
指針從局部變量中加載到rax
中。mov rdi, rax
:將this
指針(現在在rax
中)移動到rdi
中,以便調用基類構造函數時使用。call A::A() [base object constructor]
:調用基類A
的構造函數。此時,this
指針指向B
對象,但調用的是A
的構造函數,因此它會正確初始化A
的部分。mov rax, QWORD PTR [rbp-8]
:再次將this
指針從局部變量中加載到rax
中。mov DWORD PTR [rax+4], 1
:將1
賦值給this->a
。這里的[rax+4]
指的是b
位于對象內偏移量 4 的位置。 (因為B繼承A,B所以內存結構上,前四個字節是A.a ,然后才是B.a ,所以是移動到偏移4位置之后在賦值)
?