2010-12-17 68 views
1

假設我編譯源文件文件,其中包含這一段代碼,類型定義,(二進制)代碼重複和對象文件

struct Point 
{ 
    int x; 
    int y; 
}; 

struct Size 
{ 
    int x; 
    int y; 
}; 

由於PointSize是完全一樣的(在它的成員的存儲器佈局方面),編譯器是否會在目標文件中生成重複代碼(每個代碼爲struct)?這是我的第一個問題。


現在,讓我們刪除源代碼中的struct Size,並使用typedef代替,像這樣定義它,

typedef Point Size; 

什麼會編譯現在怎麼辦?它會重複代碼(因爲typedef不僅僅是重命名,而是更多)?


現在假設我們有一個類模板是這樣的:

template <int UnUsed> 
class ConcreteError : public BaseError { 
public: 
    ConcreteError() :BaseError(), error_msg() {} 

    ConcreteError (int errorCode, int osErrorCode, const std::string& errorMessage) 
     :BaseError(errorCode, osErrorCode, errorMessage){} 
}; 

然後我們設置一些定義,這樣,

typedef ConcreteError<0> FileError; 
typedef ConcreteError<1> NetworkError; 
typedef ConcreteError<2> DatabaseError; 

因爲模板參數int UnUsed未使用類的實現(只是假設),所以看起來這種情況與具有完全相同內存佈局的多個類完全相同(類似於的情況和struct Size),對象文件中是否會有重複的代碼?

而如果我們做這樣的,

typedef ConcreteError<0> FileError; 
typedef ConcreteError<0> NetworkError; 
typedef ConcreteError<0> DatabaseError; 

是這種情況更好,因爲現在我們在使用的typedef相同實例類?

PS:這個類模板代碼就是從這裏取:

How to create derived classes from a base class using template programming in C++?


其實,我沒有編譯器如何生成的源代碼的目標文件的任何想法,以及如何處理類名稱,它是成員,其他符號和所有。它如何處理typedefs?這是什麼做這個,

typedef int ArrayInt[100]; 

ArrayInt一個類型嗎?代碼編譯器在目標文件中爲它創建了什麼? 100在哪裏存儲?

回答

2

您的示例中沒有任何一行會在對象文件中生成任何代碼。或者更確切地說,它根本不會生成任何數據。我認爲「代碼」僅僅意味着處理器指令。

目標文件中的數據分爲三部分:代碼,靜態數據和常量數據。

代碼由實際函數定義(帶有函數體,不僅僅是聲明)生成,除了內聯函數。內聯函數每次實際使用時都會生成代碼。模板函數在實例化時生成代碼,但多個實例通常通過編譯器,鏈接器或兩者都優化到單個實例中。

靜態數據是通過定義全局變量,靜態成員變量(再次,實際定義,而不僅僅是類中的聲明)和靜態局部變量生成的。一個變量不能用const修飾符聲明以轉到靜態數據段。

常量數據由與靜態數據相同類型的變量聲明生成,但使用const修飾符,加上浮點文字加字符串文字加上根據硬件平臺可能更多的文字。操作系統可能實際上不允許對硬件級別的常量數據進行寫入訪問,因此如果您嘗試在該處寫入某些內容,則您的程序可能因訪問衝突或分段錯誤而崩潰

我對這種低級別的東西並不是真正的專家,所以我可能錯過了一些東西,但我想我描述的總體情況非常好。

除非我真的錯過了什麼,否則程序中沒有其他任何東西會在對象文件中生成任何數據,特別是聲明和類型定義。這些由編譯器在內部使用。所以當編譯器看到一個結構定義時,它會記住它包含兩個32位整數。當它發現一些使用該結構的真實代碼時,它知道它必須生成能夠使用兩個32位整數的代碼,必須分配至少8個字節來存儲它,等等。但所有這些信息在編譯時內部使用,並不真正進入目標文件。如果C++有類似反射的東西,那將是另一回事。

請注意,儘管定義了很多結構體並不會爲您的對象文件添加任何內容,但它可能會增加編譯器本身的內存使用量。所以你可能會說定義相同的東西會導致在編譯時數據重複,但不會在運行時。

2

首先,沒有爲您包含的第一個結構定義生成代碼,所以比較這兩種類型是沒有意義的。但在C++中,鍵入名稱很重要,所以struct A肯定與struct B明顯不同。

typedef創建類型別名,所以typedef-ed類型確實是原始類型(它不創建不同類型)。

ConcreteError<0>是與ConcreteError<1>不同的類型。

我不認爲任何東西都可以防止編譯器變得怪異,並且當參數在數據佈局方面相同時函數不需要調用數據上的其他子函數時,是不同的實際類型,功能對這兩種類型 做相同的事情,但我不認爲這在實踐中是真的做到了 。實際上有編譯器可以執行某些操作(請參閱下面的Ben的註釋)。

對於最後一個typedef(全部爲別名ConcreteError<0>),只創建一個ConcreteError「版本」(因爲只有該版本被實例化)。

+0

@lijie:你能解釋爲什麼沒有爲第一個結構定義生成代碼? – Nawaz 2010-12-17 17:51:10

+0

,因爲它們只是類型定義。實際上,他們只是告訴編譯器類型名稱的含義。你實際上並沒有定義變量或函數(它實際上會生成代碼)。 – lijie 2010-12-17 17:57:37

+0

@liljie:我以爲你會認爲我在我的代碼中使用了這些結構。即定義了一些變量,操縱它們,並做各種事情! – Nawaz 2010-12-17 18:07:27

0

不,沒有重複的代碼與未使用的PODS。如果使用它們,將會有兩個整數,並且可能會在內存中分配一些填充。他們當然會看起來都一樣,所以你想要調用的是有爭議的,但它不會比在兩個地方使用同一類型更「重複」。

不,沒有重複的代碼與別名。實際上沒有任何代碼。

也許,取決於編譯器是否使用某些優化。

也許吧。取決於您的typedefs是否在不同的翻譯單元中使用,以及您的編譯器在刪除重複實例時的表現如何。

不,這是int [100]的別名。

很大程度上,「這個結構產生多少機器代碼」的問題完全取決於實現。

相關問題