[英]C++ compiler - resolving name of a class member
當編譯器看到這段代碼時:
SomeClass foo;
int x = foo.bar;
檢索bar值的過程是什么? 即它看一些表示類定義的數據結構? 如果是這樣的數據結構是在編譯時還是在運行時生成的?
編譯器的地址為foo
。 在該地址處,有足夠的空間用於成員變量( sizeof(SomeClass)
),其中可能包含一些填充。
它知道`bar在類中的某個位置(通常是它們被聲明的順序,加上一些其他魔法,如繼承),並跳轉到該偏移量。
那是:
struct SomeClass
{
short s;
float f;
int bar;
char *c;
}
// pseudo-code:
&SomeClass.bar == (&SomeClass) + sizeof(short) + sizeof(float);
在運行時,它獲取該數據,並將其分配給x
在編譯時,編譯器將有一些數據結構,告訴它如何訪問SomeClass的每個成員。 對於簡單的情況,它只是一個偏移量,但如果你有非平凡的繼承,可能會有更多。
為了處理表達式,編譯器會查詢此內部數據並(最終)發出適當的機器代碼。 通過運行時,這個結構將被拋棄,剩下的就是從foo的地址開始,為了做任何需要而發出的代碼。 但是,如果你有一個指向bar的成員指針,那么如何訪問bar成員的細節在某種程度上被封裝在那個指針值中(可能是一個偏移,也許更復雜)。
當編譯器看到SomeClass
的定義時,該過程開始。 基於該定義,它構建了一個內部結構,其中包含SomeClass
字段的類型,以及SomeClass
方法的代碼位置。
當你寫SomeClass foo;
編譯器找到與SomeClass
的構造函數對應的代碼,並創建調用該代碼的機器指令。 在下一行,你寫int x = foo.bar
。 這里編譯器編寫機器指令為int
分配堆棧空間,然后查看SomeClass
數據結構。 該數據結構將告訴它從foo
對象的開頭到bar
字節偏移量。 然后編譯器寫入機器代碼以將對應於bar
的字節復制到x
的存儲器中。 所有這些機器代碼都寫入您的可執行文件。
通常,一旦編譯完成,表示SomeClass
和其他定義的數據結構將被丟棄。 你剩下的只是一套機器指令。 這些指令在您實際運行程序時執行,因此SomeClass
的構造函數和將foo.bar
復制到x
的代碼由CPU執行,而不會明確了解對象的結構。
這是一般情況。 當您在調試器下運行代碼並進行優化時,會出現一些特殊情況,但這通常會發生這種情況。
您必須認為在編譯期間,每個類都會轉換為結構(以簡化說明),所以如果您有
class Foo
{
int x, y, z;
char bar[10];
... etc ...
}
它們被轉換為具有指定大小的結構,在這種情況下為4 * 3 + 10個字節。 然后它根據對齊方式以更方便的方式排列它們,記住例如在偏移4處你可以找到屬性y,而在地址8你可以找到z。
然后它很簡單,只需將4添加到分配所涉及的類的地址,然后獲取y的地址,依此類推。
編譯器僅在編譯時存儲此類類元數據。 你的第一個問題是,它如何檢索bar的值,實際上非常復雜。 您可以將其視為計算bar與對象foo的偏移量,然后讀取該位置的內存。 然而,根據x的實際使用方式,它可以做一些不同的事情。 在某些情況下,“x”可能根本不會出現在編譯代碼中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.