CPP面向物件-標頭檔案
1 標頭檔案的佈局:
2 #define 保護
所有標頭檔案都應該有
#define
保護來防止標頭檔案被多重包含, 命名格式當是:<PROJECT>_<PATH>_<FILE>_H_
。
防禦式開頭防止重複include標頭檔案。
#ifndef COMMON_H
#define COMMON_H
//.... code
#endif //COMMON_H
Google C++規範:
為保證唯一性, 標頭檔案的命名應該基於所在專案原始碼樹的全路徑。
例如, 專案
foo
中的標頭檔案foo/src/bar/baz.h
可按如下方式保護:#ifndef FOO_BAR_BAZ_H_ #define FOO_BAR_BAZ_H_ ... #endif // FOO_BAR_BAZ_H_
也可以使用
#pragma once
3 前置宣告
儘可能地避免使用前置宣告。使用
#include
包含需要的標頭檔案即可。
前置宣告(forward declaration)是類、函式和模板的純粹宣告,沒伴隨著其定義。
前置宣告是為了降低編譯依賴,防止修改一個頭檔案引發多米諾效應。
Google C++規範:
優點:
- 節省編譯時間:多餘的
#include
會迫使編譯器展開更多的檔案,處理更多的輸入。- 節省不必要的重新編譯的時間:
#include
使程式碼因為標頭檔案中無關的改動而被重新編譯多次。缺點:
前置宣告隱藏了依賴關係,標頭檔案改動時,使用者的程式碼會跳過必要的重新編譯過程。
前置宣告可能會被庫的後續更改所破壞。前置宣告函式或模板有時會妨礙標頭檔案開發者變動其 API. 例如擴大形參型別,加個自帶預設引數的模板形參等等。
極端情況下,用前置宣告代替
#include
甚至都會暗暗地改變程式碼的含義:// b.h: struct B {}; struct D : B {}; // good_user.cc: #include "b.h" void f(B*); void f(void*); void test(D* x) { f(x); } // calls f(B*)
如果
#include
被B
和D
的前置宣告替代,test()
就會呼叫f(void*)
結論:
- 儘量避免前置宣告那些定義在其他專案中的實體.
- 函式:總是使用
#include
.- 類模板:優先使用
#include
.
4 行內函數
只有當函式只有 10 行甚至更少時才將其定義為行內函數.
當函式被宣告為行內函數之後, 編譯器會將其內聯展開, 而不是按通常的函式呼叫機制進行呼叫。
在類的宣告中定義的函式將被編譯器嘗試翻譯為行內函數。
優點:
只要內聯的函式體較小, 內聯該函式可以令目的碼更加高效. 對於存取函式以及其它函式體比較短, 效能關鍵的函式, 鼓勵使用內聯.
缺點:
濫用內聯將導致程式變得更慢. 內聯可能使目的碼量或增或減, 這取決於行內函數的大小. 內聯非常短小的存取函式通常會減少程式碼大小, 但內聯一個相當大的函式將戲劇性的增加程式碼大小. 現代處理器由於更好的利用了指令快取, 小巧的程式碼往往執行更快。
經驗:
- 不要內聯超過 10 行的函式
- 謹慎對待解構函式
- 內聯包含迴圈或
switch
語句的函式往往得不償失一般不會被內聯的函式:虛擬函式和遞迴函式不會被正常內聯
5 #include
的路徑及順序
使用標準的標頭檔案包含順序可增強可讀性, 避免隱藏依賴: 相關標頭檔案, C 庫, C++ 庫, 其他庫的 .h, 本專案內的 .h.
專案內標頭檔案應按照專案原始碼目錄樹結構排列, 避免使用 UNIX 特殊的快捷目錄 .
(當前目錄) 或 ..
(上級目錄).(即使用絕對路徑而非相對路徑)
#include
的順序:
dir2/foo2.h
(這個cpp檔案對應的.h檔案,放置於優先位置)- C 系統檔案
- C++ 系統檔案
- 其他庫的
.h
檔案 - 本專案內
.h
檔案
這種優先的順序排序保證 dir2/foo2.h
(.h檔案) 遺漏某些必要的庫時, 其實現/測試(.cpp檔案)的構建會立刻中止。這一條規則保證維護這些檔案的人們首先看到構建中止的訊息而不是別人。