1.定義
頭文件是擴展名為 .h 的文件,頭文件也是C++的源代碼,頭文件中包含了 C++中函數、類、對象等的聲明和宏定義,它可以被多個源文件通過#include引用共享。
2.使用頭文件原因
C++中有“單一定義”規則,即一個對象只能被定義一次,如果在一個源文件中定義了一個函數,其他的源文件想要使用這個函數就需要在使用前聲明一下這個函數,在編譯結束之後,編譯器鏈接的時候再去查找這些函數的定義。
故要使用其他文件中定義的函數、類、對象(變量)時,需要對函數、類、對象進行聲明。這些聲明文件一般放在一個頭文件中,這樣只要通過#include就可以一下引入所有的聲明。當然也可以在頭文件中定義宏。
自定義的頭文件,使用#include"頭文件.h"。對於標準庫頭文件的包含使用#include<頭文件.h>
3.編譯過程中的頭文件
C++代碼的編譯主要通過以下幾個過程:預編譯->編譯->彙編->鏈接【可參考:鏈接(建議先看此鏈接,再看下面內容)】,最後生成可執行文件。
在預編譯階段,編譯器將#include"頭文件.h"替換成“頭文件.h”中具體的聲明內容。
在鏈接階段,編譯器查找聲明對象的定義。
3.1.預編譯階段,頭文件被替換
我們看一個簡單的例子,下面是一個頭文件CA.h
#ifndef CA_H
#define CA_H
int Fun();
#endif
頭文件中的函數、類、對象(變量)必須在一個源文件有進行定義,這裏在A.cpp中進行定義。
A.cpp:
#include "CA.h"
int Fun()
{
return 1;
}
B.cpp中引用頭文件:
#include "CA.h"
int Fun1()
{
return Fun() + 1;
}
int main(){
return Fun1();
}
預編譯命令:
g++ -E B.cpp -o B.i
經過預處理後, B.i:
# 1 "B.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "B.cpp"
# 1 "CA.h" 1
int Fun();
# 2 "B.cpp" 2
int Fun1()
{
return Fun() + 1;
}
int main(){
return Fun1();
}
可以看到,B.cpp中的頭文件#include "CA.h"都被CA.h中的具體內容所代替。
【注】“#”是註釋
3.2.使用頭文件
g++ A.cpp B.cpp -o main
B.cpp中通過頭文件引入的東西,會在A.cpp中自動找到。
4.如何寫頭文件
參考:鏈接 在寫頭文件時需要注意,在開頭和結尾處必須按照如下樣式加上預編譯語句(如下)
Circle.h:
#ifndef CIRCLE_H
#define CIRCLE_H
// 你的代碼寫在這裏
#endif
#ifndef代表沒有定義CIRCLE_H時,才能進入if。進入if之後,第一步就是執行#define CIRCLE_H來定義CIRCLE_H。這樣做以後,即使重複引入頭文件,也不會重複執行if中的東西。
至於CIRCLE_H這個名字實際上是無所謂的,你叫什麼都行,只要符合規範都行。原則上來説,非常建議把它寫成這種形式,因為比較容易和頭文件的名字對應。
下面舉個最簡單的例子來描述一下,咱就求個圓面積。
第1步,建立一個空工程(以在VS2003環境下為例)。
第2步,在頭文件的文件夾裏新建一個名為Circle.h的頭文件,它的內容如下:
#ifndef CIRCLE_H
#define CIRCLE_H
class Circle
{
private:
double r;//半徑
public:
Circle();//構造函數
Circle(double R);//構造函數
double Area();//求面積函數
} ;
#endif
在頭文件裏,並不寫出函數的具體實現。
第3步,要給出Circle類的具體實現,因此,在源文件夾裏新建一個Circle.cpp的文件,它的內容如下:
#include " Circle.h "
Circle::Circle()
{
this->r=5.0;
}
Circle::Circle( double R)
{
this->r=R;
}
double Circle:: Area()
{
return 3.14*r*r;
}
一般實現Circle.h的cpp文件取名為Circle.cpp
最後,我們建一個main.cpp來測試我們寫的Circle類,它的內容如下:
#include < iostream >
#include " Circle.h "
using namespace std;
int main()
{
Circle c(3);
cout<<"Area="<<c.Area()<<endl;
return 1;
}
運行命令:
g++ Circle.cpp main.cpp -o main
./main
C++ 防止頭文件被重複包含的方法還有一種,即#pragma once,C++ 防止頭文件被重複包含(#pragma once 與 #ifndef 的區別)
5.C++頭文件有.h和沒有.h
iostream.h是非標準頭文件,iostream是標準頭文件形式。iostream.h時代沒有名詞空間,即所有庫函數包括頭文件iostream.h都聲明在全局域。為了體現結構層次,c++標準委員會引入了名詞空間這一概念,並把所有庫函數聲明由全局域改到了名詞空間std。iostream.h裏面定義的所有類以及對象都是在全局空間裏,所以可以直接使用cout,但如果你用iostream,就不能直接使用cout了,iostream裏面所定義的東西都在標準命名空間std裏面,所以你必須加上 using namespace std才能使用cout。
故而,
在早些時候,這兩種頭文件是等價:
#include<iostream.h> // 這個現在已經不支持了
和
#include
using namespace std;
現在標準的C++頭文件沒有.h擴展名,將以前的C的頭文件轉化為C++的頭文件後,可以加上c的前綴表示來自於c,例如cmath就是由math,h變來。
注意:c語言的string.h變為cstring,和c++的string是兩個完全不同的東西。
6.後綴為.hpp的文件
後綴為.hpp的文件一般提供程序使用的接口。
我們公司和另一家軟件公司合作,這樣就必然要互相提供一些軟件的信息(比如一些類,它到底是要做什麼的),可是在提供這些信息的同時我們又不像讓對方知道我們這些類的具體實現,畢竟這些是我們公司的算法核心和心血啊。所以這個時候就可以把類的接口(這個類是要做什麼的)放在*.hpp文件中,而具體類的實現放在 .cpp文件。這時候我們只要給對方公司.hpp文件就行了。這樣既提供了必要的信息,又保護了我們的核心代碼。