假設有一個Date類
Date.h
- class Date {
- private:
- int year, month, day;
- };
如果有個Task類的定義要用到Date類,有兩種寫法
其一
Task1.h
- class Date;
- class Task1 {
- public:
- Date getData();
- };
其二
Task2.h
- #include "Date.h"
- class Task2 {
- public:
- Date getData();
- };
一個採用前置聲明,一個採用#include<Date.h>加入了Date的定義。兩種方法都能通過編譯。但是 Task1.h 這種寫法更好。如果Date.h 的 private 成員變量改變,比如變成 double year, month, day; ,Task1.h 不需要重新編譯,而 Task2.h 就要重新編譯,更糟的是如果 Task2.h 還與其他很多頭文件有依賴關係,就會引發一連串的重新編譯,花費極大的時間。可是事實上改變一下寫法就可以省去很多功夫。所以能用前置聲明代替#include 的時候,儘量用前置聲明
有些情況不能用前置聲明代替#include
比如Task1.h改成
- class Date;
- class Task1 {
- public:
- Date d;
- };
會編譯錯誤,因爲Date d定義了一個Date類型變量,編譯器爲d分配內存空間的時候必須知道d的大小,必須包含定義Date類的Date.h文件。
這是可以採用指針來代替
- class Date;
- class Task1 {
- public:
- Date *d;
- };
指針的大小是固定的。在32位機上是4字節,64位機上是8字節。這時編譯Task1的時候不需要Date的大小,所以和Date的定義無關。
上述例子可以說明
如果使用object reference 或 object point 可以完成任務,就不要用object
這樣可以盡最大可能避免#include
爲聲明式和定義是提供不同的頭文件
在函數庫的設計過程中,接口的設計就要遵循上述準則。
一個接口的頭文件是這樣的
interface.h
- class Date;
- class Address;
- class Email;
- Date getDate();
如果客戶只用到Date類,編譯器就只會去編譯Date.h,而不去編譯Address.h,Email.h 等等文件。
究竟什麼時候需要前置聲明,什麼時候需要頭文件包含呢?
頭文件包含其實是一想很煩瑣的工作,不但我們看着累,編譯器編譯的時候也很累,再加上頭文件中常常出現的宏定義。感覺各種宏定義的展開是非常耗時間的,遠不如自定義函數來得速度。我僅就不同頭文件、源文件間的句則結構問題提出兩點原則,僅供參考:
第一個原則應該是,如果可以不包含頭文件,那就不要包含了。這時候前置聲明可以解決問題。如果使用的僅僅是一個類的指針,沒有使用這個類的具體對象(非指針),也沒有訪問到類的具體成員,那麼前置聲明就可以了。因爲指針這一數據類型的大小是特定的,編譯器可以獲知。
第二個原則應該是,儘量在CPP文件中包含頭文件,而非在頭文件中。假設類A的一個成員是是一個指向類B的指針,在類A的頭文件中使用了類B的前置聲明並編譯成功,那麼在A的實現中我們需要訪問B的具體成員,因此需要包含頭文件,那麼我們應該在類A的實現部分(CPP文件)包含類B的頭文件而非在聲明部分(H文件)包含。