谷歌C 程式設計規範(一) 標頭檔案

2021-06-28 12:44:01 字數 4409 閱讀 7694

今天晚上在網上發現了谷歌的c++程式設計規範這個東西,大家貌似都非常推崇的樣子。然後又在網上搜了一下,好像並沒有中文版的。所以,我就打算把它們全都翻譯一遍啦,既能提高自己的英語水平,還有助於養成良好的程式設計風格,何樂而不為呢?哈哈~

整個程式設計規範包括九個部分,所以就分成九篇文章來寫啦。本篇文章主要是背景介紹以及c++標頭檔案的程式設計規範,下面直接進入正題。

背景:

c++是谷歌很多開源專案的主要開發語言。每乙個c++程式猿都知道,c++有許多非常強大的特性,但是同時,這些強大的特性又會增加**的複雜性,讓**更易錯,並且變得難以閱讀和維護。所以,寫這篇文章的目的就是要通過描述在用c++編碼時那些該做的和不該做的事情來控制語言的複雜性。下面的這些規則在允許程式猿高效地使用c++語言特性的同時保證了**的基本可控性。

編碼風格,通常叫做可讀性,是我們對於管理**時所用規範 的總稱。其實「編碼風格」這個詞是不太恰當的,因為這些編碼規範並不僅僅指原始檔的規整。我們保持**基本可控性的方法之一就是要求實行前後一致性。這是非常重要的,因為只要這樣才能保證乙個程式猿能閱讀另乙個程式猿的**,並且快速理解它。保持統一的編碼風格並且遵循相應的規範意味著我們能夠用「模式匹配」去推斷變數標示符。運用逗號,要求的慣用語法和模式能夠讓**變得更加易懂。當然,在有些情況下,改變確定的編碼風格也許會取得更好的效果,但是我們選擇不這樣做,因為我們要保持編碼的前後一致性。

本篇文章想要表達的另一件事情是c++語言特性的膨脹。c++是一門擁有許多高階特性的非常龐大的語言。有時候,我們限制甚至禁止某些特性的使用,藉此保持**的簡潔並且避免這些特性將會導致的各種各樣的錯誤和問題。這篇指導列舉了這些特性並且說明了為什麼要限制對於它們的使用。

谷歌的開源專案都要求遵從這篇指導。並且需要注意的是,這篇指導並不是c++的教程:我們假設讀者已經對c++非常熟悉了。

標頭檔案:

一般來說,每個.cc檔案都有與之相關聯的.h檔案。當然,也存在不少的例外,比如單元測試或者那些僅包含乙個main函式的小的.cc檔案。對於標頭檔案的正確使用能對你的**的可讀性,效能,體量都帶來極大的改變。接下來就的這些規則就會列出使用標頭檔案時容易犯的各種各樣的錯誤。

#define 防護

每個標頭檔案都要定義#define以防止多重包含,並且採用___h這樣的形式。為了保證唯一性,它們應當基於專案的原始檔樹的絕對路徑進行命名。比如專案foo中的檔案foo/src/bar/baz.h就應當使用如下的定義形式:

#ifndefine foo_bar_baz_h

#define foo_bar_baz_h

.....

#endif //foo_bar_baz_h

標頭檔案依賴性

當你用前置宣告能夠滿足需求時,不要使用#include。當你包含標頭檔案的時候,你就引進了檔案的依賴性,也就是說,當你包含的標頭檔案改變時,當前檔案的**要全部重新編譯。並且,當你的標頭檔案包含了其他標頭檔案的時候,這些標頭檔案的改變,都會導致包含你的標頭檔案的**重新編譯。因此,我們要最小化減少標頭檔案的包含,特別是在標頭檔案中包含其他標頭檔案。

最好的在標頭檔案中少包含的標頭檔案的方法就是使用前置宣告。比如,當你的標頭檔案中要使用類file,但又無需知道類file的具體宣告。那麼你可以直接前置宣告class file而不是#include "file/base/file.h"。那麼我們如何使用類file而無需通過它的定義呢?

1.宣告資料型別成員為foo*或foo&

2.我們可以宣告(而不是定義)引數或者返回值型別為foo的函式。(除了引數foo或者const foo&有乙個non-explicit的單參建構函式,這種情況下我們需要完整的定義以支援自動型別轉換)

3.我們可以宣告資料型別為foo的靜態變數。因為靜態資料成員在類的定義之外定義。

另外,你必須包含foo標頭檔案,如果你的類以foo為子類或者包含有型別為foo類的資料成員。

有時候,使用指標成員(最好是scoped_ptr)而不是物件成員是更好的選擇。但是,這對**的可讀性和效能會造成損害。所以,如果僅僅是為了減少標頭檔案的包含,最好盡量避免這種形式的轉換。當然,一般情況下,.cc檔案是需要知道它使用類的具體定義的並且需要包含相應的標頭檔案。

注:當你在原始檔中使用foo標示符時,要通過#include或者前置宣告來引入。而且不要依賴那些通過間接標頭檔案包含獲得的標示符。除了,如果在myfile.h中#include(或者前置宣告了)foo,那麼在myfile.cc中仍可使用。

內聯函式:

當函式很小,一般來說10行以內,可以將其定義為內聯。

定義:當宣告函式為內聯時,編譯器會將其擴充套件為內聯而不是用通常的函式呼叫機制去使用它們。

優點:如果內聯函式足夠小的話,那麼將它內聯化將大大提高目標**的效率。對於訪問器,修改器或者其他短小的,對效能要求較高的**,可自由使用內聯。

缺點:事實上,如果過度使用內聯,反而會讓程式變慢。根據函式的大小,內聯可以讓**量增加或者減少。當內聯乙個非常小的訪問器函式時,**量通常會減少。反之,內聯乙個較大的函式時反而會增加**量。在現代處理器上,簡短的**通常執行得更快,因為它們能更好地利用快取記憶體。

選擇:比較合適的選擇是,當**行數超過10行時就不選擇內聯了。我們可以想到析構函式,它們通常比看起來要長,因為它們需要呼叫成員和基類的建構函式。另一點需要注意的是:如果內聯函式中有迴圈或switch語句時並不是非常高效。(除非,通常情況下,迴圈或switch語句並不執行)而且,即使被宣告為內聯,該函式也不一定作為內聯函式執行。比如,虛函式或者遞迴函式不被缺省內聯。通常,遞迴函式不應該被內聯。將虛函式內聯的主要目的是將它定義在類的內部,為了方便或者記錄它們的行為。

.inl.h檔案:

當你要定義複雜的內聯函式的時候,你可能會用到字尾為-inl,h的檔案。一般內聯函式的宣告都放在標頭檔案中,這樣在函式呼叫的時候就編譯器就會將其作為內聯函式。但是函式的實現**一般都放在.cc檔案中。事實上,我們不希望有太多的實現**放在.h檔案中,除非這能帶來更好的可讀性或效能。如果內聯函式的定義非常短小,並且包含很少的邏輯,那麼你應該將它放在.h檔案中。比如,訪問器和修改器的**就應當放在類的定義中。當然,如果為了實現和呼叫的方便,更複雜的內聯函式也能放在.h檔案中,儘管這會讓.h檔案顯得非常笨拙。因此,將它們放在-inl.h中可能是更好的選擇。這能夠將內聯函式的實現和類的定義分開,並且仍能夠在需要的時候將實現包含進去。

-inl.h檔案的另乙個作用是模板的定義。它能讓模板的定義更加易讀。別忘了,和其他標頭檔案一樣,-inl.h同樣需要使用#define防止重定義。

函式引數順序:

當定義乙個函式的時候,引數的順序應當是先輸入後輸出。

在c/c++中,函式的引數無非是輸入,輸出或者兩者皆有。輸入引數一般都是值或者const型別的引用。而輸出或者輸入/輸出引數通常都為non-const的指標。我們在放置函式引數的時候,應當將只用於輸入的引數放在輸出引數前面。特別地,不要將新增加的引數放在引數的最後。如果新增加的引數是只用於輸入的,那麼將它放在輸出引數的前面。

這並不是必須遵守的規則,那些既是輸入又是輸入的引數(通常為class/struct)會將這一切全都搞亂,並且有時候為了和相關函式保持一致性,你也需要違背這條規則。

命名和包含的順序:

使用標準的包含順序不僅可以是為了可讀性,更加可以避免隱藏的庫之間的依賴性,包括c庫,c++庫,其他庫,以及你自己的專案的標頭檔案。專案的所有標頭檔案都應當按照專案源目錄的降序排列,並且不能使用unix的目錄快捷方式.(當前目錄)和..(父目錄)。比如,google-awesome-project/src/base/logging.h應該按如下方式被包含:

#include "base/logging.h"
dir/foo.cc主要用於實現或是測試dir/foo2.h中的事務,那麼你應當按如下順序包含:

1.dir2/foo2.h

2.c系統檔案

3.c++系統檔案

4.其他庫的標頭檔案

5.你自己專案的標頭檔案

這樣的優先順序順序降低了隱藏的依賴性。我們希望每個標頭檔案都能自己編譯。而實現它最好的方式就是每個標頭檔案都是某些.cc檔案中第乙個被#include的.h檔案。通常dir/foo.cc和dir2/foo2.h會在同乙個目錄下(又如,base/basictypes_test.cc和basetypes.h),但是它們也可以不在乙個目錄下。在每一類庫的內部,標頭檔案又應當按照字典序排列。比如,google-awesome-project/src/foo/internal/fooserver.cc就應當按如下方式包含:

#include "foo/public/fooserver.h"

#include #include #include #include #include "base/basictypes.h"

#include "base/commandlineflags.h"

#include "foo/public/bar.h"

注:某些地方翻譯地不是很好,原文中有些地方也比較囉嗦晦澀。

C 基礎(一)標頭檔案

建立乙個自定義列表 如何建立乙個註腳 注釋也是必不可少的 katex數學公式 新的甘特圖功能,豐富你的文章 uml 圖表 flowchart流程圖 匯出與匯入 你好!這是你第一次使用markdown編輯器所展示的歡迎頁。如果你想學習如何使用markdown編輯器,可以仔細閱讀這篇文章,了解一下mar...

谷歌C 程式設計規範筆記

新公司程式設計規範遵循google code style c 利用了點時間閱讀了一遍,將自認為重要的記錄下來。1使用 define包含 可以參考部落格 你所不知道的事兒 ifndef endif不能防止編譯器將檔案包含兩次 格式如下 例如,專案foo中的標頭檔案foo src bar baz.h,可...

C89 標頭檔案

雙引號 搜尋使用者所在目錄,如果找不到搜尋系統指定的目錄 尖括號 僅僅搜尋系統指定目錄 include 設定插入點 include 字元處理 include 定義錯誤碼 include 浮點數處理 include 檔案輸入 輸出 include 引數化輸入 輸出 include 資料流輸入 輸出 i...