天天看點

Google C++ 代碼規範一.頭檔案二.作用域三.類四.函數**五.命名約定六.注釋七.C++的一些特性

Google C++ 代碼規範一.頭檔案二.作用域三.類四.函數**五.命名約定六.注釋七.C++的一些特性

本文參考 Google開源項目風格指南 ,由于原文篇幅過長,本文對其進行精簡,讀者可以通過右側目錄進行導航閱讀。

本文對一些重點進行了紅色标注 ,同時為了便于了解,還進行了大量舉例。

一.頭檔案

通常每一個

.cc

檔案都有一個對應的

.h

檔案. 也有一些常見例外, 如單元測試代碼和隻包含

main()

函數的

.cc

檔案.

#define 保護

所有頭檔案都應該使用

#define

來防止頭檔案被多重包含, 命名格式當是:

<PROJECT>_``<PATH>``_``<FILE>``_H_

例如:

#ifndef FOO_BAR_BAZ_H_

#define FOO_BAR_BAZ_H_

#endif // FOO_BAR_BAZ_H_

前置聲明

所謂「前置聲明」(forward declaration)是類、函數和模闆的純粹聲明,沒伴随着其定義.

- 盡量避免前置聲明那些定義在其他項目中的實體.
  - 函數:總是使用 `#include`.
  - 類模闆:優先使用 `#include`.
           

内聯函數

當函數被聲明為内聯函數之後, 編譯器會将其内聯展開, 而不是按通常的函數調用機制進行調用.

隻有當函數隻有 10 行甚至更少時才将其定義為内聯函數.

包含檔案的名稱及次序

将包含次序标準化可增強可讀性、避免隐藏依賴(hidden dependencies,注:隐藏依賴主要是指包含的檔案編譯),次序如下:

1. 目前cpp檔案對應的.h檔案
     1. C 系統檔案
     1. C++ 系統檔案
     1. 其他庫的 .h 檔案
     1. 本項目内 .h 檔案
           

舉例來說,google-awesome-project/src/foo/internal/fooserver.cc 的包含次序如下:

#include “foo/public/fooserver.h” // 優先位置

   #include <sys/types.h> //C系統檔案

   #include <unistd.h> //C系統檔案

#include <hash_map> /_/C++系統檔案 _

   #include _//C++系統檔案 _

#include “base/basictypes.h” //其他庫.h檔案

   #include “base/commandlineflags.h” //其他庫.h檔案

   #include “foo/public/bar.h” //本項目的.h檔案**

二.作用域

命名空間

命名空間将全局作用域細分為獨立的, 具名的作用域, 可有效防止全局作用域的命名沖突.

- 遵守 命名空間命名 中的規則
     - 在命名空間的最後注釋出命名空間的名字
           

例如:

// .h 檔案

namespace mynamespace {

// 所有聲明都置于命名空間中

// 注意不要使用縮進

class MyClass {

public:

void Foo();

};

} // namespace mynamespace

- 不應該使用 _using 訓示_ 引入整個命名空間的辨別符号,例如: ~~using namespace~~~~ ~~~~std~~~~;~~
     - 不要在頭檔案中使用 命名空間别名  例如:~~ namespace baz = ::foo::bar::baz;~~
     - 禁止用内聯命名空間  例如: ~~inline namespace foo{...}~~
           

三.類

構造函數的職責

構造函數中不允許調用虛函數,如果代碼允許,應在構造函數出錯直接終止程式,否則使用Init函數進行初始化

删除預設拷貝構造和指派運算符

如果定義的類型明确不允許使用拷貝和指派操作,需要使用delete關鍵字禁用

例如 :

MyClass(const MyClass&) = delete; //禁用拷貝構造

MyClass& operator=(const MyClass&) = delete; //禁用指派運算符

結構體和類的使用時機

僅當隻有資料成員時使用

struct

, 其它一概使用

class

.

繼承

- 所有繼承必須是 `public` 的. 如果你想使用私有繼承, 你應該替換成把基類的執行個體作為成員對象的方式
  - 析構函數應聲明為 virtual 
  - 對于可能被子類通路的成員函數, 不要過度使用 `protected` 關鍵字. 注意, 資料成員都必須是 [私有的](https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/classes/#access-control).
  - 對于重載的虛函數或虛析構函數, 使用 `override`, 或 (較不常用的) `final` 關鍵字顯式地進行标記
  - 不允許多繼承,如果需要多繼承,除第一個類外,其他都應該是接口類型
           

運算符重載

除少數特定環境外, 不要重載運算符. 也不要建立使用者定義字面量.

四.函數**

- 将所有輸入參數(不修改值的參數)放在輸出參數(會修改值并傳回給調用方的參數)之前,常數參數要使用 const 修飾 
  - 所有引用參數必須加上const
           

例如 : void Example(``const int arg1`` ,``const``int& arg2``,void* arg3``);

- 如果需要重載函數,應寫在臨近的代碼位置
  - 預設參數應該置于非預設函數之後
           

五.命名約定

通用命名規則

函數命名, 變量命名, 檔案命名要有描述性; 少用縮寫.

檔案命名

檔案名要全部小寫, 可以包含下劃線 (

_

) 或連字元 (

-

), 依照項目的約定. 如果沒有約定, 那麼 “

_

” 更好.

例如 :

- `my_useful_class.cc`
  - `my-useful-class.cc`
           

類型命名

類型名稱的每個單詞首字母均大寫, 不包含下劃線,如:

MyExcitingClass

,

MyExcitingEnum

變量命名

變量 (包括函數參數) 和資料成員名一律小寫, 單詞之間用下劃線連接配接. 類的成員變量以下劃線結尾, 但結構體的就不用,不使用匈牙利命名法,駝峰命名法

普通變量

如:

a_local_variable

,

a_struct_data_member

,

a_class_data_member_

類成員變量

類成員變量最後應該接下劃線_

如:

class TableInfo {

private:

string table__name__ ; // 好 - 後加下劃線.

string tablename_ ; // 好.

static Pool* pool_ ; // 好.

};

結構體變量

結構體的成員變量可以和普通變量一樣,, 不用像類那樣接下劃線:

如:

struct UrlTableProperties {

string name;

int num_entries;

static Pool* pool;

};

常量命名

常量命名需要以小寫字母“k”開頭,不使用下劃線,單詞首字母大寫,

如: const int kDaysInAWeek = 7;

函數命名

函數命名采用 駝峰命名規則,即單詞首字母大寫,不适用下劃線

如: AddTableEntry()

DeleteUrl()

OpenFileOrDie()

命名空間命名

命名空間以小寫字母命名,并遵守通用命名規則最高層的命名空間的名字取決于項目名稱。命名空間中的代碼,應該存放在和命名空間的名字比對的檔案夾中

枚舉命名

枚舉的命名應和宏一緻,全部使用大寫字母,單詞間使用下劃線分隔

如:enum AlternateUrlTableErrors {

OK = 0,

OUT_OF_MEMORY = 1,

MALFORMED_INPUT = 2,

};

宏命名

宏命名全部使用大寫字母,單詞間使用下劃線分隔,如 :

#define ROUND(x) …

#define PI_ROUNDED 3.0

六.注釋

注釋風格

注釋使用 // 或都可以,與團隊保持一緻

檔案注釋

每個檔案開頭應加入版權公告,如果檔案僅僅是一些測試代碼,可以不使用檔案注釋

法律公告和作者資訊

每個檔案都應該包含許可證引用. 為項目選擇合适的許可證版本.(比如, Apache 2.0, BSD, LGPL, GPL)

檔案内容

如果一個

.h

檔案聲明了多個概念(類型), 則檔案注釋應當對檔案的内容做一個大緻的說明, 同時說明各概念之間的聯系. 一個一到兩行的檔案注釋就足夠了, 對于每個概念的詳細文檔應當放在各個概念中, 而不是檔案注釋中.

類注釋

每個類的定義都要附帶一份注釋, 描述類的功能和用法, 除非它的功能相當明顯.

如:

// Iterates over the contents of a GargantuanTable.

// Example:

// GargantuanTableIterator* iter = table->NewIterator();

// for (iter->Seek(“foo”); !iter->done(); iter->Next()) {

// process(iter->key(), iter->value());

// }

// delete iter;

class GargantuanTableIterator {

};

函數注釋

函數聲明處的注釋描述函數功能; 定義處的注釋描述函數實作

函數聲明

函數聲明處注釋的内容:

- 函數的輸入輸出.
     - 對類成員函數而言: 函數調用期間對象是否需要保持引用參數, 是否會釋放這些參數.
     - 函數是否配置設定了必須由調用者釋放的空間.
     - 參數是否可以為空指針.
     - 是否存在函數使用上的性能隐患.
           

舉例如下:

// Returns an iterator for this table. It is the client’s

// responsibility to delete the iterator when it is done with it,

// and it must not use the iterator once the GargantuanTable object

// on which the iterator was created has been deleted.

//

// The iterator is initially positioned at the beginning of the table.

//

// This method is equivalent to:

// Iterator* iter = table->NewIterator();

// iter->Seek("");

// return iter;

// If you are going to immediately seek to another place in the

// returned iterator, it will be faster to use NewIterator()

// and avoid the extra seek.

Iterator* GetIterator() const;

對于簡單的函數可簡單注釋

函數定義

如果函數的實作比較巧妙或複雜,應該注釋清函數的實作思路

變量注釋

通常變量名本身足以很好說明變量用途. 某些情況下, 也需要額外的注釋說明.

七.C++的一些特性

類型轉換

不要使用強制類型轉換或隐式類型轉換,應該使用C++的類型轉換 ,如 static_cast

自增和自減

對于疊代器和其他模闆對象使用前置形式的自增或自減 ,因為前置自增效率相比後置自增效率更高。如 ++i ,–i,++it,–it 等

預處理宏

- 盡量不要在.h檔案中定義宏
  - 盡量在馬上要使用時才進行#define,使用後要立即#undef
  - 不要隻是對已經存在的宏使用#undef,選擇一個不會沖突的名稱;
  - 不要試圖使用展開後會導緻 C++ 構造不穩定的宏, 不然也至少要附上文檔說明其行為.
  - 盡量不要用##處理函數,類和變量的名字
           

空值,0 , nullptr 和 NULL

- 整數使用0,實數使用0.0
  - 指針用nullptr或者NULL
  - 字元串使用 '\0'
           

sizeof的使用

盡量使用sizeof(varname)而不是sizeof(type)

auto的使用

- auto隻能在局部變量裡使用,不要用在檔案作用域
  - 如果auto 表達式的右值是一個對象,使用auto& ,例如 auto& person = Person();
  - 推薦在疊代周遊中使用 auto
           

lambda表達式

- 不要使用預設捕獲,捕獲要顯示寫出來 ,比起 `[=](int x) {return x + n;}`, 您該寫成 `[n](int x) {return x + n;}` 才對
  - lambda表達式應該盡量簡短,函數體不應過長(不超過10行)
  - lambda表達式的傳回值應該以尾置傳回類型的方式寫出來,例如 [](int x, int y)->int{return x+y;};
           

與零值的判斷

- 整數與零值的判斷應寫為 if (num == 0);
  - 浮點數與零值的判斷應寫為 if (num <0.00001&&num>-0.00001); 不能寫成 if(num == 0.0);
  - 布爾值與零值判斷應盡量寫為 if(bool == false);而不是if(!bool);
           

繼續閱讀