問題01:如何確保頭文件只包含了一次
在你的頭文件中使用 #define 來定義一個宏,並且在沒有這個宏被定義時包含這個頭文件中的內容。你可以使用 #ifndef、#define 和 #endif 預處理指令集合。
- #ifndef MYCLASS_H__
- #define MYCLASS_H__
- // Put everything here ...
- #endif
問題02:如何保證一個跨多個源文件的變量只有一個實例存在
在一個單一的實現文件中以一種通常的方式來聲明和定義這個變量,並且在其他的實現文件中使用extern關鍵字來訪問這個變量。關鍵字extern告訴編譯器這個變量的存儲空間已經在別的地方分配。關鍵字extern也告訴鏈接器它查驗的這個變量存在與別的目標文件中,並且當鏈接器創建最後的可執行文件或庫時,他需要到別的文件中找到這個變量。如果鏈接器永遠都不能找到你聲明的這個外部變量,或者它找到這個變量的兩個或兩個以上的定義,那麼就會報一個鏈接錯誤。
問題03:當你引用了別的頭文件中的類,如何減少編譯依賴
在可能需要避免不必要的編譯依賴的地方使用前向類聲明。一個前向類聲明是一個忽略細節的方法,而這些細節又是你不需要關心的(即你不需要調用該類的方法或訪問該類的成員變量)。
如果你在一個頭文件Myclass.h中使用#include來包含其他類的頭文件的話,你可能需要使用六、七個類的頭文件。一旦它們中的任何一個有更新,那麼所有包含Myclass.h的實現文件不得不重新編譯。
問題04:如何避免不同模塊中的變量名產生衝突
使用命名空間(namespace)來模塊化代碼。使用命名空間,你就能吧一些大的分佈在多個獨立文件中的代碼組成到一個單一的命名空間中。並且你也可以依據需要來嵌套使用命名空間來把一個大的模塊分成多個子模塊,並且你的模塊的用戶就可以有選擇地使用他們需要的存在於你的命名空間中的元素。
使用命名空間別名可以避免輸入冗長的路徑。
- using dev = handware::devices;
- dev::Device d;
但是你需要明智地使用命名空間,下面是當你需要使用命名空間時的一些流行指導意見:
>> 保守地使用using namespace xxx。引入一個完整的命名空間增加了名字衝突的可能,並且 這樣也降低了有命名空間提供的模塊性。
>> 在頭文件不用使用using語句。頭文件被很多其他文件所包含,因此如果你在一個頭文件中使用一個命名空間或者命名空間的東西時,你就給任何包含這個頭文件的文件暴露了你正在使用的任何東西。解決這個問題的方法是在這個頭文件中使用完整的名字路徑來限制它。
>> 在#include指令前面不要使用using聲明或者定義。如果你這樣做的話,在這個頭文件中你就會暴露你正在使用的任何東西,這肯定不是這個頭文件作者的本意。
問題05:假如你有很多需要變成內聯函數的成員函數或者獨立函數,如何保證聲明和實現分離
創建一個.inl文件,並且在你的頭文件的尾部使用#include來包含它。這樣完成的功能和你吧函數的定義放在頭文件的尾部是等價的。
- // Value.h
- #ifndef VALUE_H__
- #define VALUE_H__
- #include <string>
- class Value {
- public:
- Value(const std::string& val) : val_(val) {}
- std::string getVal() const;
- private:
- std::string val_;
- };
- #include "Value.inl"
- #endif VALUE_H__
- //Value.inl
- inline std::string Value::getVal() const { return(val_); }