const物件為什麼可以在標頭檔案中定義

2022-04-13 07:40:01 字數 2439 閱讀 2939

對於標頭檔案中為什麼可以定義const變數(或物件),以及推薦用const代替#define巨集定義,之前一直概念不清晰,今天就總結一下。

之前在網上查過,解釋的都不太到位,或者角度不一樣(從編譯原理、強弱定義?),總之不能清晰理解,發現《c++ primer》上基本上涵蓋了所有平常遇到的c/c++問題,而且《c++ primer》地位正宗,上面的解釋可信度自然沒得說。so,有問題,先找《c++ primer》。

1.const物件預設為檔案的區域性變數。《c++ primer 4》p86,

2.標頭檔案用於宣告而不是用於定義。《c++ primer 4》p100,原因是:

1.定義和宣告的區別:定義只可以出現一次,而宣告則可以出現多次。下面這些是定義(怎樣判斷的?),都不應該出現在標頭檔案中:

extern int ival = 10;

double fica_rate;

同乙個程式中有兩個以上檔案含有上述任乙個定義都會導致多重定義鏈結錯誤。編譯時可能不會報錯,因為編譯時每個原始檔都是單獨編譯生成目標檔案,彼此是相互獨立不可見的,只有在鏈結的時候整個工程才作為乙個整體,鏈結主要解決模組間的相互引用問題,而在編譯階段生成目標檔案是,會暫時擱置那些外部引用,外部引用是在鏈結時進行確定的。

因為標頭檔案包含在多個原始檔中,所以不應該含有變數或函式的定義。但是……

3.對於標頭檔案不應該含有定義這一規則,有三個例外。標頭檔案中可以定義類、值在編譯時就已知道的const物件

(陣列的大小可以用const int型變數定義,這在c中是不行的),和inline函式。《c++ primer 4》p100,在補充乙個,

模板"必須"定義在標頭檔案中,包括模板類和模板函式。

下面來解釋原因:

1.這些實體可以在多個原始檔中定義,只要每個原始檔中的定義是相同的。

2.在標頭檔案中定義這些實體,是因為編譯器需要他們的定義(不只是宣告)來產生**(這句話對模板同樣適用)。例如:為了產生能定義或使用類的物件的**,編譯器需要知道組成該型別的資料成員。同樣還需要知道能夠在這些物件上執行的操作。類定義提供所需要的資訊。當然(但是)類定義內部的成員可以是宣告,類定義和類內部成員的定義不是一回事哦,可以不同步進行,也」不建議「類定義時定義類內部函式。在標頭檔案中定義const物件則需要更多解釋。

3.下面是本節的重點了(引自《c++ primer 4》,滿滿的都是乾貨):

const 變數預設時是定義該變數的檔案的區域性變數。正如我們現在所看到的,這樣設定預設情況的原因在於允許 const 變數定義在標頭檔案中。

在 c++ 中,有些地方需要放置常量表示式。例如,列舉成員的初始化式必須是常量表示式。在以後的章節中將會看到其他需要常量表示式的

例子。一般來說,常量表示式是編譯器在編譯時就能夠計算出結果的表示式。當 const 整型變數通過常量表示式自我初始化時,這個 const 整型變數就可能是常量表示式。而 const 變數要成為常量表示式,初始化式必須為編譯器可見。 為了能夠讓多個檔案使用相同的常量值,const 變數和它的初始化式必須是每個檔案都可見的。而要使初始化式可見,一般都把這樣的 const 變數定義在標頭檔案中。那樣的話,無論該 const 變數何時使用,編譯器都能夠看見其初始化式。

但是,c++ 中的任何變數都只能定義一次(第 2.3.5 節)。定義會分配儲存空間,而所有對該變數的使用都關聯到同一儲存空間。因為 const 物件預設為定義它的檔案的區域性變數,所以把它們的定義放在標頭檔案中是合法的。 

這種行為有乙個很重要的含義:當我們在標頭檔案中定義了 const 變數後,每個包含該標頭檔案的原始檔都有了自己的 const 變數,其名稱和值都一樣。 

當該 const 變數是用常量表示式初始化時,可以保證所有的變數都有相同的值。但是在實踐中,大部分的編譯器在編譯時都會用相應的常量表示式替換這些 const 變數的任何使用。所以,在實踐中不會有任何儲存空間用於儲存用常量表示式初始化的 const 變數。 

如果 const 變數不是用常量表示式初始化,那麼它就不應該在標頭檔案中定義(若不初始化const變數,這個const變數有什麼用呢?)。相反,和其他的變數一樣,該 const 變數應該在乙個原始檔中定義並初始化。應在標頭檔案中為它新增 extern 宣告,以使其能被多個檔案共享。

下面再補充點關於編譯和鏈結方面的知識:

編譯:將預處理生成的檔案,經過詞法、語法、語義分析及優化後編譯成若個目標模組(linux中的 .o 或win32中的 .obj)。

編譯可以理解為,將高階語言翻譯成計算機可以理解的二進位制**,即機器語言。編譯器需要的是語法正確,函式與變數的宣告正確。

鏈結時主要鏈結函式和全域性變數,目標檔案之間相互鏈結自己所需要的函式和全域性變數,而函式可能來自其他目標檔案或庫檔案。

靜態鏈結

載入時動態鏈結

執行時動態鏈結

關於 const 中定義的復合型別 為什麼可以改變

對於 const 問題回答時 我會回答 const 定義的是乙個唯讀的常量 而且定義的資料 不能改變 一旦宣告變數必須馬上初始化 同時沒有變數提公升 會造成暫時性死區 只在宣告的塊級 作用中有效 const ac 1 ac 2 報錯assignment to constant variable.if...

物件指標為NULL,為什麼還是可以呼叫成員函式

b要理解這個的話。成員函式其實可以認為是乙個普通的函式,比如 1 2 3 4 class a 在編譯器看來,大概就長這個樣子吧 1 void a func a this,int x 注意此時y是成員變數,編譯器會自動給它加上this 也就是 1 void a func a this,int x 此時...

exe為什麼可以在不同機器上執行

1,很多資料都說,exe檔案是二進位製碼 指令 是可以直接被機器執行的 2,但是,所謂的二進位製碼 指令 不是因機而異的嗎?不同的機器,使用的cpu是不一樣的,指令集也不一樣,那為什麼在一台機器上生成的exe可以在別的不同指令集上的cpu上執行?3,在我看來,這個所謂的指令,恐怕指的並不是對應硬體的...