2010-03-20 56 views
11

您好我有一個關於this指針的問題,當一個對象被構造時,它被初始化了嗎?這意味着什麼時候可以使用它?虛擬表是在構造函數中構造的,是否與this指針相同?什麼時候在C++中初始化「this」指針?

例如,我有這樣的代碼。輸出是8.這是否意味着在構造函數被輸入之前,this指針已經被初始化了?

class A{ 
    public: 
    A() { cout<<sizeof(*this);} 
    int i; 
    int *p; 
}; 

int main() {  
    A a; 
} 

如果它是真的,那麼在輸入構造函數之前還會發生什麼?

如果不是這樣,this指針何時初始化?

+5

編譯器知道* this的大小,它會生成一個硬編碼的值。也許你需要一個更好的例子。 – 2010-03-20 15:57:34

+2

sizeof是編譯時的東西(我不會說硬編碼),並且不依賴於運行時「this」指針中包含的值。另外,vtable(應該有一個)不是「在構造函數中」構造的。 Neil: – 2010-03-20 16:02:33

+0

:真的嗎?如果不是int構造函數,那是什麼? – skydoor 2010-03-20 16:12:25

回答

13

this指針不是對象或類的成員 - 它是您調用的方法的隱式參數。因此,它的傳遞方式與其他參數非常相似 - 除非您不直接詢問它。

在上例中,構造函數是一種特殊的方法,它又是一種特殊的函數。當你構造對象,編譯器分配它的內存(在這種情況下,在棧上,如amain函數的局部變量。然後,它會自動調用構造函數初始化對象。

作爲調用的一部分構造函數,隱參數this - 一個指向你的對象 - 傳遞作爲參數

在具有以下簽名的方法...

void MyMethod (const int* p) const; 

其實有兩個參數,兩個指針。 。有顯式參數p和隱式參數this。該行末尾的const指定this是一個常量指針,與前面的指定p是一個常量指針一樣。因爲this是隱式傳遞的,所以只能存在對這種特殊語法的需求,所以不能像其他參數那樣以常規方式指定常量。

「靜態」方法沒有隱含的「this」參數,並且無法直接訪問對象成員 - 可能沒有與該調用關聯的特定對象。它基本上是一個標準函數,而不是一個方法,除了訪問私有成員(提供它可以找到一個要訪問的對象)。

正如Steve Fallows指出的那樣,sizeof (this)在編譯時是已知的,因爲它是一個指針類型,所有指針(* 1)具有相同的sizeof值。你看到的「8」意味着你正在編譯一個64位平臺。this此時可用 - 它指向有效內存,並且所有成員都已完成其構造函數調用。但是,它不一定是完全初始化的 - 畢竟你仍然在構造函數中。

編輯

* 1 - 嚴格,這可能不是真的 - 但是編譯器知道它在這裏處理什麼類型的指針與即使該值直到運行時才知道。

+0

+1,很好的解釋。在調用約定的情況下:this這個:http://msdn.microsoft.com/en-us/library/984x0h58%28v=VS.71%29。aspx – 2010-03-20 17:28:20

2

sizeof(* this)在編譯時已知。所以cout聲明沒有揭示這個初始化。

鑑於構造函數可以立即開始訪問該對象的成員,顯然這是在構造函數開始之前初始化的。

在構造函數之前發生了什麼?很可能是任何事情。我不認爲標準限制了編譯器可以做什麼。也許你應該指定你想要的任何事情。

7

this指針未被存儲。當爲佔用特定內存位置的對象調用構造函數時,該位置將作爲參數傳遞給構造函數和其他成員函數。

如果this會存儲在對象內部,如何檢索那個指針?對,您需要再次使用this指針:)

+0

只是爲了在需要的情況下添加一些解釋 - 當您訪問方法內的對象成員時,編譯器在後臺使用'this'來查找對象和成員。 'member1'和'this-> member1'實際上是一樣的東西 - 第一種形式只是簡寫。 – Steve314 2010-03-20 16:37:52

+0

+1爲你的最後語句發送給我一個無限遞歸 – Dario 2010-03-20 16:40:10

0

該指針是每個類的調用(包括構造函數)的第一個參數。

當一個類方法被調用時,類的地址被最後壓入堆棧(假設這裏是cdecl調用約定)。這被讀回寄存器以用作這個指針。

構造函數實際上被稱爲好像它們是普通的成員函數。

你不能有一個虛擬的構造函數,因爲構造函數負責設置vtable成員。

+0

以上的一些或全部可能適用於你的特定編譯器,但你所說的都不是C++標準指定的。 – 2010-03-20 16:09:51

2

虛擬表是在構造函數中構造的,與這個指針是一樣的嗎?

虛擬表不在構造函數中構造。
通常,同一個類的所有實例共享一個單一的全局v-表,每個單獨的類都有它自己的全局v-表。
v表在編譯時已知,在程序加載時「已構造」。

this指針「構造」(我認爲「分配」是一個更好的詞)在分配時,也就是global new operator調用後,並進入構造函數之前。

在對象被堆棧分配而不是堆分配的情況下,全局變量new未被調用,但this仍然可用,因爲在輸入構造函數之前分配了棧空間。

實例vptr在分配對象的內存之後以及構造函數被調用之前分配。

+2

vtable不是在那裏構建的,但是指向vtable(vptr)的指針仍然需要填寫。編輯 – kennytm 2010-03-20 16:09:04

+0

以提及分配之後和構建之前分配的vptr。 – abelenky 2010-03-20 16:12:28

+0

所有這一切都假定有一個vtable--一個編譯器可以以任何想要的方式實現運行時分派,只要它達到預期的結果即可。這可能意味着在運行時每次調用都會修改某種數據結構(也許是爲了讓最常見的調用運行得更快) - 雖然我在這裏是「在理論上」而不是「在現實中」,因爲它很難打敗效率的虛擬表。 – Steve314 2010-03-20 16:16:43

0

正如nobugz已經指出的那樣,您的示例沒有多大意義 - sizeof根據您傳遞給它的對象的類型得出結果。它在而不是在運行時評估其操作數。

這就是說,是的,this在進入ctor之前被初始化。基本上,編譯器爲對象分配空間(如果對象具有自動存儲持續時間,則在堆棧上,或者如果它具有動態存儲持續時間,則使用::operator new)。進入ctor後,基類的ctors(如果有的話)已經運行完畢。當您調用ctor時,this會給出爲對象分配的內存地址。

0

這個開始指向當前對象,並且在輸入構造函數體之前,所有成員和基類都已經初始化。

因此,你可以伸手指向在初始化列表,但接收方應該做的不是別的,只是將它存儲等,因爲所指向的情況下可能無法完全在當時建造。

#include <iostream> 

class B; 

class A 
{ 
    B* b_ptr; 
public: 
    A(B* b); 
}; 

class B 
{ 
    A a; 
    int i; 
public: 
    B(): a(this), i(10) {} 
    void foo() const { std::cout << "My value is " << i << '\n'; } 
}; 

A::A(B* b): 
    b_ptr(b) //Ok to store 
{ 
    b_ptr->foo(); //not OK to use, will access initialized member 
} 

int main() 
{ 
    B b; 
} 
2

這是否意味着在進入構造函數之前,該this指針已初始化?

,則this指針的值是稱爲構造甚至被稱爲前。此值可通過構造函數中的this關鍵字,構造函數initialization lists,析構函數,成員方法獲得。 this關鍵字在表面上作爲(指針類型的)方法變量行事,但不是一個;它通常位於寄存器(x86平臺上的ecx),通常不能編譯像&this這樣的代碼。

進入

的構造函數之前會發生些什麼,至少至於this指針而言,出現這種情況(除非使用placement new)的第一件事是分配的內存最終由this指向,無論是在堆棧上(如你的例子)還是堆上(使用new)。此時this指針是已知的。然後,默認構造函數或明確指定的構造函數(通過構造函數初始化列表)然後在基類(如果有的話)和您的類非POD成員變量(如果有)上調用。類vtable指針也設置在此點之前,如果您的類包含虛擬方法或析構函數。然後,你的類構造函數體,如果有的話,被調用。 (構造函數是遞歸調用的,即當調用基類的構造函數時,調用後者的基類構造函數,然後調用非POD成員構造函數,並設置基類'vtable指針,接着是類的構造函數體。