淺談頭文件(.h)和源文件(.cpp)的區別
本人原來在大一寫C的時候,都是所有代碼寫在一個文件里一鍋亂煮。經過自己開始寫程序之后,發現一個工程只有一定是由多個不同功能、分門別類展開的文件構成的。一鍋亂煮只會導致代碼可讀性差、維護性差。但是本人在學習C++的時候,被這章節弄混了。 Source & Header, Separate Files from Classes,里面介紹了一個對於Python寫手來說很新的概念。就是頭文件和源文件的區別,下面談談我自己學習后的理解。
頭文件(.h)
頭文件用來寫 類的聲明 (包括類的成員的聲明和方法聲明)、函數原型、#define 常數等,但是很少會寫出具體的實現和細節。就好比抽象類一樣。
頭文件很有意思的是,開頭和結尾必須按照以下格式:
#ifndef MYCLASS_H
#define MYCLASS_H
// code here
#endif
當時我看到這個是極其的不理解和迷茫的,后來閱讀了別人的博文才略懂。
首先解釋他是干嘛使的,這是防止頭文件被重復引用。什么叫被重復引用?就是同一個頭文件(.h)在同一個源文件(.cpp)中被include了多次。這種錯誤常常是因為include嵌套。舉個最簡單的例子,存在cellphone.h這個頭文件引用了#include "huawei.h",之后又有china.cpp這個源文件同時導入了#include "cellphone.h" 和 #include "huawei.h"。此時huawei.h就在一個源文件里引用了兩次。
那么,某些時候,只是因為include了兩遍,增大了編譯器的工作量。如果是小型程序的話還好說,但是大型工程甚至會增長幾個小時的編譯時間。但是另一些情況,會引起很嚴重的錯誤。比如在頭文件中定義了全局變量會引起重復定義。
所以就有了我們上面那些看起來亂七八糟的代碼,下面開始解釋。
#ifndef MYCLASS_H 的意思是 if not define myclass.h,這樣看就很好理解了,如果引用這個頭文件的源文件不存在myclass.h這個頭文件,那么接下行 #define MYCALSS_H, 引入myclass.h。然后就是我們頭文件的代碼。如果已經有了,直接跳到 #endif。
理論上來說,上面這個片段的MYCLASS_H是可以任意命名的,但是約定俗成的,為了可讀性的,我們都把它命名為這個頭文件的大寫和下划線的形式。如下面這一段代碼:
#ifndef HUAWEI_H // 防止huawei.h被重復引用
#define HUAWEI_H
#include <cmath> // 引用標准庫
#include "honor.h" // 引用非標准庫頭文件
...
void Function(); // 全局函數聲明
class Mate20{ // 類聲明
public: Mate20(); // 構造函數聲明
~Mate20(); // 析構函數聲明
private:
protected:
};
#endif
上面的代碼其實已經很好的解釋了頭文件的作用——聲明。可以看見內部的函數和方法僅僅是聲明,而都沒有寫入具體細節。
源文件(.cpp)
源文件主要寫實現頭文件中已經聲明的那些函數的具體代碼。需要注意的是,開頭必須#include一下實現的頭文件,以及要用到的頭文件。那么當你需要用到自己寫的頭文件中的類時,只需要#include進來就行了。
下面舉個例子
我首先寫一個頭文件Pen.h,里面定義了一個Pen類:
#ifndef PEN_H
#define PEN_H
#include <string>
class Pen
{
public:
Pen(std::string brand);
};
#endif
然后我在我的主文件test.cpp中引入了這個頭文件:
#include <iostream>
#include "Pen.h"
using namespace std;
Pen::Pen(string brand) {
cout << "What brand are you looking for?" << endl;
cout << brand;
}
int main() {
Pen pilot("pilot");
}
// 輸出結果為
/* What brand are you looking for?
pilot */
結果表明,這樣引入是正確的。