通常,在乙個c++程式中,只包含兩類檔案——.cpp檔案和.**件。其中,.cpp檔案被稱作c++原始檔,裡面放的都是c++的源**;而.**件則被稱作c++標頭檔案,裡面放的也是c++的源**。
c+ +語言支援「分別編譯」(separate compilation)。乙個程式包含多個.cpp檔案時,這些.cpp檔案可分別編譯成各自的目標檔案,後面再將這些目標檔案做一次鏈結(link)就行了。.**件不需要編譯。
標頭檔案即.**件,該檔案包含了對程式中用到的所有函式的宣告。區分兩個概念:
定義:「定義」就是把乙個符號完完整整地描述出來:它是變數還是函式,返回什麼型別,需要什麼引數等等。定義分配記憶體空間。
宣告:「宣告」則只是宣告這個符號的存在,即告訴編譯器,這個符號是在其他檔案中定義的,我這裡先用著,你鏈結的時候再到別的地方去找找看它到底是什麼吧。宣告不分配記憶體空間。
定義的時候要按c++語法完整地定義乙個符號(變數或者函式),而宣告的時候就只需要寫出這個符號的原型了。 需要注意的是,乙個符號,在整個程式中可以被宣告多次,但卻要且僅要被定義一次。
若乙個專案中定義了很多函式,這些函式又被很多.cpp檔案呼叫,這樣每個.cpp檔案中都要宣告這些函式。標頭檔案的作用就是把所有函式在乙個***.**件裡宣告,每個呼叫這些函式的.cpp檔案只需要#include"***.h"標頭檔案即可。
乙個完整的程式通常包含多個定義函式的.cpp檔案,乙個main.cpp,乙個或多個.h標頭檔案。這些檔案存放需要在同一路徑下。
#include 是乙個來自c語言的巨集命令,它在編譯器進行編譯之前,即在預編譯的時候就會起作用。
#include的作用是把它後面所寫的那個檔案的內容,完完整整地、 一字不改地包含到當前的檔案中來。值得一提的是,它本身是沒有其它任何作用與副功能的,它的作用就是把每乙個它出現的地方,替換成它後面所寫的那個檔案的 內容。簡單的文字替換,別無其他。因此,main.cpp檔案中的第一句(#include 「math.h」),在編譯之前就會被替換成math.**件的內容。
標頭檔案的作用就是被其他的.cpp包含進去的,標頭檔案中應該只放變數和函式的宣告,而不能放它們的定義。即,只能在標頭檔案中寫形如:extern int a;和void f();的句子。這些才是宣告。如果寫上int a;或者void f() {}這樣的句子,那麼一旦這個標頭檔案被兩個或兩個以上的.cpp檔案包含的話,編譯器會立馬報錯。(關於extern,前面有討論過,這裡不再討論定義跟 宣告的區別了。)
但是,這個規則是有三個例外的。
一,標頭檔案中可以寫const物件的定義。因為全域性的const物件默 認是沒有extern的宣告的,所以它只在當前檔案中有效。把這樣的物件寫進標頭檔案中,即使它被包含到其他多個.cpp檔案中,這個物件也都只在包含它的 那個檔案中有效,對其他檔案來說是不可見的,所以便不會導致多重定義。同時,因為這些.cpp檔案中的該物件都是從乙個標頭檔案中包含進去的,這樣也就保證 了這些.cpp檔案中的這個const物件的值是相同的,可謂一舉兩得。同理,static物件的定義也可以放進標頭檔案。
二,標頭檔案中可 以寫內聯函式(inline)的定義。因為inline函式是需要編譯器在遇到它的地方根據它的定義把它內聯展開的,而並非是普通函式那樣可以先宣告再鏈 接的(內聯函式不會鏈結),所以編譯器就需要在編譯時看到內聯函式的完整定義才行。如果內聯函式像普通函式一樣只能定義一次的話,這事兒就難辦了。因為在 乙個檔案中還好,我可以把內聯函式的定義寫在最開始,這樣可以保證後面使用的時候都可以見到定義;但是,如果我在其他的檔案中還使用到了這個函式那怎麼辦 呢?這幾乎沒什麼太好的解決辦法,因此c++規定,內聯函式可以在程式中定義多次,只要內聯函式在乙個.cpp檔案中只出現一次,並且在所有的.cpp文 件中,這個內聯函式的定義是一樣的,就能通過編譯。那麼顯然,把內聯函式的定義放進乙個標頭檔案中是非常明智的做法。
三,標頭檔案中可以寫類 (class)的定義。因為在程式中建立乙個類的物件時,編譯器只有在這個類的定義完全可見的情況下,才能知道這個類的物件應該如何布局,所以,關於類的 定義的要求,跟內聯函式是基本一樣的。所以把類的定義放進標頭檔案,在使用到這個類的.cpp檔案中去包含這個標頭檔案,是乙個很好的做法。在這裡,值得一提 的是,類的定義中包含著資料成員和函式成員。資料成員是要等到具體的物件被建立時才會被定義(分配空間),但函式成員卻是需要在一開始就被定義的,這也就 是我們通常所說的類的實現。一般,我們的做法是,把類的定義放在標頭檔案中,而把函式成員的實現**放在乙個.cpp檔案中。這是可以的,也是很好的辦法。 不過,還有另一種辦法。那就是直接把函式成員的實現**也寫進類定義裡面。在c++的類中,如果函式成員在類的定義體中被定義,那麼編譯器會視這個函式為 內聯的。因此,把函式成員的定義寫進類定義體,一起放進標頭檔案中,是合法的。注意一下,如果把函式成員的定義寫在類定義的標頭檔案中,而沒有寫進類定義中, 這是不合法的,因為這個函式成員此時就不是內聯的了。一旦標頭檔案被兩個或兩個以上的.cpp檔案包含,這個函式成員就被重定義了。
考 慮一下,如果標頭檔案中只包含宣告語句的話,它被同乙個.cpp檔案包含再多次都沒問題——因為宣告語句的出現是不受限制的。然而,上面討論到的標頭檔案中的 三個例外也是標頭檔案很常用的乙個用處。那麼,一旦乙個標頭檔案**現了上面三個例外中的任何乙個,它再被乙個.cpp包含多次的話,問題就大了。因為這三個 例外中的語法元素雖然「可以定義在多個原始檔中」,但是「在乙個原始檔中只能出現一次」。設想一下,如果a.h中含有類a的定義,b.h中含有類b的定 義,由於類b的定義依賴了類a,所以b.h中也#include了a.h。現在有乙個原始檔,它同時用到了類a和類b,於是程式設計師在這個原始檔中既把 a.h包含進來了,也把b.h包含進來了。這時,問題就來了:類a的定義在這個原始檔**現了兩次!於是整個程式就不能通過編譯了。你也許會認為這是程式 員的失誤——他應該知道b.h包含了a.h——但事實上他不應該知道。
使用"#define"配合條件編譯可以很好地解決這個問題。在一 個頭檔案中,通過#define定義乙個名字,並且通過條件編譯#ifndef…#endif使得編譯器可以根據這個名字是否被定義,再決定要不要繼 續編譯該頭文中後續的內容。這個方法雖然簡單,但是寫標頭檔案時一定記得寫進去。 條件編譯可參考
參考博文
C 標頭檔案的作用
標頭檔案 每個c c程式通常分為兩個檔案。乙個檔案用於儲存程式的宣告 declaration 稱為標頭檔案。另乙個檔案用於儲存程式的實現 implementation 稱為定義 definition 檔案。c c程式的標頭檔案以 h 為字尾,c程式的定義檔案以 c 為字尾,c 程式的定義檔案通常以 ...
C語言標頭檔案的作用
c99中規定,所有頂層的預設儲存類標誌符都是extern 老子又猜對了 標頭檔案中宣告的函式,預設都是extern字首。但是為了我們程式設計師方便,我們採取下面的手段 我個人認為是extern催生了標頭檔案的誕生。在沒有標頭檔案的情況下,所有本.c檔案引用的外部函式定義,都要在自己.c檔案內宣告 並...
C 標頭檔案 h的作用
收到c語言,必然會用到.h檔案,它有什麼樣的作用?整理各網路資料,如下基本功能描述 理論概述 h中一般放的是同名 c檔案中定義的變數 陣列 函式的宣告,需要讓 c外部使用的宣告。1 h檔案作用 1.方便開發 包含一些檔案需要的共同的常量,結構 型別定義,函式 變數申明 2.使函式的作用域從函式宣告的...