歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> C++編碼規范(Google C++ Style Guide)

C++編碼規范(Google C++ Style Guide)

日期:2017/3/1 9:55:03   编辑:Linux編程

I 關於編碼規范

1.最重要的一點:明確規范,然後保持一致。BE CONSISTENT!

II Header Files

1.The #define Guard

Guard的命名規則為<PROJECT>_<PATH>_<FILE>_H_。比如foo工程中的文件foo/src/bar/baz.h應定義如下Guard:

#ifndef FOO_BAR_BAZ_H_

#define FOO_BAR_BAZ_H_

...

#endif // FOO_BAR_BAZ_H_

2.頭文件依賴

在頭文件中盡量使用forward declaration替代#include,這樣可以減少頭文件之間的相互依賴,從而可能使得增量編譯時需要重新編譯的編譯單元減少。

如果要在一個頭文件中使用類Foo,則在以下情況下,可以僅使用forward declaration:

1)聲明類型為Foo *或Foo &的數據成員。

2)聲明參數或返回類型為Foo的函數。

3)聲明類型為Foo的靜態數據成員。因為靜態數據成員是在類定義之外定義的。

如果在源文件(.cpp或.cc)中需要使用符號Foo,應該直接在源文件中使用#include引入Foo的定義,而不是依賴於其它頭文件的間接包含。一個例外是,如果Foo在myfile.cc中使用,可以在myfile.h中包含相應頭文件。

3.內聯函數

只有在函數足夠小時才定義內聯函數,比如小於10行。

不要把析構函數聲明為內聯函數,因為析構函數往往比你編寫的要長得多,因為編譯器會插入相關成員的析構操作。

不要將函數有loop和switch的函數聲明的內聯函數。

有些函數即使聲明為內聯函數也不會內聯,比如虛函數和遞歸函數。

4.The -inl.h Files

由於內聯函數需要在頭文件中實現,這樣編譯器才能在相應函數被調用的地方插入內聯函數實現代碼。

對於簡單的內聯函數,比如accessors和mutators,應該在.h文件中定義。

更復雜的內聯函數應該在單獨的-inl.h文件中定義。

使用-inl.h文件的另一種情況是函數模板的定義,從而使得你的模板定義更具可讀性。

5.函數參數的順序

C++的函數參數有三種類型:input, output, or both. 規則是將輸入參數放在輸出參數之前。

通常輸入參數應該是值類型或const引用,輸出參數是非const指針。

6.Names and Order of Includes

頭文件應分組包含,通常以如下順序包含頭文件:類定義頭文件,C庫,C++庫,其它三方庫,工程定義頭文件。

所有工程定義的頭文件,應該以工程源碼根目錄為起點,列出完整路徑,絕不應該使用"."和".."來表示以當前目錄為基准的相對路徑。

例:

#include "foo/public/fooserver.h" // Preferred location.

#include <sys/types.h>

#include <unistd.h>

#include <hash_map>

#include <vector>

#include "base/basictypes.h"

#include "base/commandlineflags.h"

#include "foo/public/bar.h"

III Scoping

1.命名空間

命名空間定義結束時應使用注釋標明對應的命名空間。

1.1 Unnamed namespaces

鼓勵在.cc文件中使用Unnamed namespaces,這樣可以防止運行時命名沖突。通常將局部於.cc文件的一些helper函數放在Unnamed namespace中。

絕不在.h文件中使用Unnamed namespace.

namespace { // This is in a .cc file.

// The content of a namespace is not indented

enum { kUnused, kEOF, kError }; // Commonly used tokens.

bool AtEof() { return pos_ == kEOF; } // Uses our namespace's EOF.

} // namespace

1.2 Named namespaces

在頭文件和forward declarations之後,命名空間包含整個源文件。

絕不使用using指令導入整個命名空間,以免造成命名空間污染。可以使用using導入單個符號。

using指令可以出現在.cc文件中的任何位置,以及頭文件的函數、方法和類中。

1.3 Namespace alias

命名空間別名可以出現在.cc文件中的任何位置,頭文件中包含整個頭文件的命名空間內,以及頭文件中的函數和方法中。

如果在.h中使用了命名空間別名,則包含此頭文件的文件也可以見到此別名,因此在頭文件中使用命名空間別名應慎重。

// Shorten access to some commonly used names in .cc files.

namespace fbz = ::foo::bar::baz;

// Shorten access to some commonly used names (in a .h file).

namespace librarian {

// The following alias is available to all files including

// this header (in namespace librarian):

// alias names should therefore be chosen consistently

// within a project.

namespace pd_s = ::pipeline_diagnostics::sidetable;

inline void my_inline_function() {

// namespace alias local to a function (or method).

namespace fbz = ::foo::bar::baz;

...

}

} // namespace librarian

2.嵌套類

不要使用public nested class,除非你確定它是接口的一部分。這種情況下直接定義這個類(不定義為嵌套類)通常更直觀。更進一步,可以使用命令空間來達到將這個類和全局作用域隔離的目的。

3.Nonmember, Static Member, and Global Functions

有一些函數不屬於任何類或者不適合作為某個類的成員,則最好的處理方式是將其定義為非成員函數,並聲明一個命名空間來管理這些函數。不要僅為了組織一組函數而定義一個類。

如果這些函數只在當前.cc文件中使用,則可以使用unnamed namespace或者聲明為static linkage。

如果這些函數共享某些靜態數據,則應該將它們定義為一個新的類的靜態成員函數。

盡量不使用全局函數。

4.局部變量

讓局部變量的作用域盡可能小,並且在定義的時候初始化,而不是先定義再賦值。

應該在使用變量之前才定義變量,不使用以前C風格的在函數一開始定義所有局部變量的方式。

5.靜態變量和全局變量

絕不使用類類型的靜態變量或全局變量,因為對象創建和銷毀順序的不確定性往往造成難以發現的bug。

具有靜態存儲 (static storage duration)的對象,包括靜態變量、全局變量、類的靜態成員變量和函數內的靜態變量,都應該是“平凡數據對象 (POD, Plain Old Data)”:ints, chars, floats, 指針或者以 POD為元素的arrays/structs 。

C++規范沒有完備地說明靜態成員初始化式的調用順序,因此不要用函數調用來初始化靜態POD變量,除非調用的函數不依賴於其它全局的東西。

對象的銷毀順序與創建順序相反,因為創建順序不確定,所以銷毀的順序也是不確定的(例如,在程序結束時,一個靜態變量可能已經被銷毀了,但是代碼依然在執行(可能在另一個線程中)並且試圖訪問這個靜態變量)。結果是靜態變量只允許為POD,這一規則完全排除了將vector(使用C風格數組)和string(使用const char[])作為靜態變量使用。

如果一定要將一個類對象作為靜態或全局的,應該使用指針(不會被釋放),然後在main函數初始化(或者使用pthread_once()初始化)。要注意這個指針應該是“平凡”的指針(raw pointer),而不是智能指針(smart pointer)。

Copyright © Linux教程網 All Rights Reserved