C++編程規(guī)范

C++用法很多,包容性也比較強(qiáng)寿弱。一個(gè)C++的工程可能包含了各種各樣沒見過的用法渗磅。本篇內(nèi)容主要是參照谷歌C++標(biāo)準(zhǔn)規(guī)范嚷硫,結(jié)合自身實(shí)際工作
及經(jīng)驗(yàn)检访,整理一份適合平時(shí)C++開發(fā)的規(guī)則,規(guī)范自身C++編程規(guī)范仔掸。詳細(xì)內(nèi)容可參考Google C++風(fēng)格指南脆贵。

1 函數(shù)

1.1 參數(shù)順序

總述

函數(shù)的參數(shù)順序?yàn)? 輸入?yún)?shù)在先, 后跟輸出參數(shù).

說明

C/C++ 中的函數(shù)參數(shù)或者是函數(shù)的輸入, 或者是函數(shù)的輸出, 或兼而有之. 輸入?yún)?shù)通常是值參或 const引用, 輸出參數(shù)或輸入/輸出參數(shù)則一般為非 const 指針. 在排列參數(shù)順序時(shí), 將所有的輸入?yún)?shù)置于輸出參數(shù)之前. 特別要注意, 在加入新參數(shù)時(shí)不要因?yàn)樗鼈兪切聟?shù)就置于參數(shù)列表最后, 而是仍然要按照前述的規(guī)則, 即將新的輸入?yún)?shù)也置于輸出參數(shù)之前.

這并非一個(gè)硬性規(guī)定. 輸入/輸出參數(shù) (通常是類或結(jié)構(gòu)體) 讓這個(gè)問題變得復(fù)雜. 并且, 有時(shí)候?yàn)榱似渌瘮?shù)保持一致, 你可能不得不有所變通.

1.2 編寫簡(jiǎn)短函數(shù)

總述

我們傾向于編寫簡(jiǎn)短, 凝練的函數(shù).

說明

我們承認(rèn)長(zhǎng)函數(shù)有時(shí)是合理的, 因此并不硬性限制函數(shù)的長(zhǎng)度. 如果函數(shù)超過 40 行, 可以思索一下能不能在不影響程序結(jié)構(gòu)的前提下對(duì)其進(jìn)行分割.

即使一個(gè)長(zhǎng)函數(shù)現(xiàn)在工作的非常好, 一旦有人對(duì)其修改, 有可能出現(xiàn)新的問題, 甚至導(dǎo)致難以發(fā)現(xiàn)的 bug. 使函數(shù)盡量簡(jiǎn)短, 以便于他人閱讀和修改代碼.

在處理代碼時(shí), 你可能會(huì)發(fā)現(xiàn)復(fù)雜的長(zhǎng)函數(shù). 不要害怕修改現(xiàn)有代碼: 如果證實(shí)這些代碼使用 / 調(diào)試起來很困難, 或者你只需要使用其中的一小段代碼, 考慮將其分割為更加簡(jiǎn)短并易于管理的若干函數(shù).

1.3 引用參數(shù)

總述

所有按引用傳遞的參數(shù)必須加上 const.

定義

在 C 語(yǔ)言中, 如果函數(shù)需要修改變量的值, 參數(shù)必須為指針, 如 int foo(int *pval). 在 C++ 中, 函數(shù)還可以聲明為引用參數(shù): int foo(int &val).

優(yōu)點(diǎn)

定義引用參數(shù)可以防止出現(xiàn) (*pval)++ 這樣丑陋的代碼. 引用參數(shù)對(duì)于拷貝構(gòu)造函數(shù)這樣的應(yīng)用也是必需的. 同時(shí)也更明確地不接受空指針。

缺點(diǎn)

容易引起誤解, 因?yàn)橐迷谡Z(yǔ)法上是值變量卻擁有指針的語(yǔ)義起暮。

結(jié)論

函數(shù)參數(shù)列表中, 所有引用參數(shù)都必須是 const:

void Foo(const string &in, string *out);

1.4 函數(shù)重載

總述

若要使用函數(shù)重載, 則必須能讓讀者一看調(diào)用點(diǎn)就胸有成竹, 而不用花心思猜測(cè)調(diào)用的重載函數(shù)到底是哪一種. 這一規(guī)則也適用于構(gòu)造函數(shù)卖氨。

定義

你可以編寫一個(gè)參數(shù)類型為 const string& 的函數(shù), 然后用另一個(gè)參數(shù)類型為 const char* 的函數(shù)對(duì)其進(jìn)行重載:

class MyClass {
  public:
    void Analyze(const string &text);
    void Analyze(const char *text, size_t textlen);
};

優(yōu)點(diǎn)

通過重載參數(shù)不同的同名函數(shù), 可以令代碼更加直觀. 模板化代碼需要重載, 這同時(shí)也能為使用者帶來便利。

缺點(diǎn)

如果函數(shù)單靠不同的參數(shù)類型而重載 (acgtyrant 注:這意味著參數(shù)數(shù)量不變), 讀者就得十分熟悉 C++ 五花八門的匹配規(guī)則, 以了解匹配過程具體到底如何. 另外, 如果派生類只重載了某個(gè)函數(shù)的部分變體, 繼承語(yǔ)義就容易令人困惑.

結(jié)論

如果打算重載一個(gè)函數(shù), 可以試試改在函數(shù)名里加上參數(shù)信息. 例如, 用 AppendString() 和 AppendInt() 等, 而不是一口氣重載多個(gè) Append(). 如果重載函數(shù)的目的是為了支持不同數(shù)量的同一類型參數(shù), 則優(yōu)先考慮使用 std::vector 以便使用者可以用 列表初始化 指定參數(shù)鞋怀。

1.5 函數(shù)返回類型后置語(yǔ)法

總述

只有在常規(guī)寫法 (返回類型前置) 不便于書寫或不便于閱讀時(shí)使用返回類型后置語(yǔ)法.

定義

C++ 現(xiàn)在允許兩種不同的函數(shù)聲明方式. 以往的寫法是將返回類型置于函數(shù)名之前. 例如:

int foo(int x);

C++11 引入了這一新的形式. 現(xiàn)在可以在函數(shù)名前使用 auto 關(guān)鍵字, 在參數(shù)列表之后后置返回類型. 例如:

auto foo(int x) -> int;

后置返回類型為函數(shù)作用域. 對(duì)于像 int 這樣簡(jiǎn)單的類型, 兩種寫法沒有區(qū)別. 但對(duì)于復(fù)雜的情況, 例如類域中的類型聲明或者以函數(shù)參數(shù)的形式書寫的類型, 寫法的不同會(huì)造成區(qū)別.

優(yōu)點(diǎn)

后置返回類型是顯式地指定 Lambda 表達(dá)式 的返回值的唯一方式. 某些情況下, 編譯器可以自動(dòng)推導(dǎo)出 Lambda 表達(dá)式的返回類型, 但并不是在所有的情況下都能實(shí)現(xiàn). 即使編譯器能夠自動(dòng)推導(dǎo), 顯式地指定返回類型也能讓讀者更明了.

有時(shí)在已經(jīng)出現(xiàn)了的函數(shù)參數(shù)列表之后指定返回類型, 能夠讓書寫更簡(jiǎn)單, 也更易讀, 尤其是在返回類型依賴于模板參數(shù)時(shí). 例如:

template <class T, class U> auto add(T t, U u) -> decltype(t + u);

對(duì)比下面的例子:

template <class T, class U> decltype(declval<T&>() + declval<U&>()) add(T t, U u);

缺點(diǎn)

后置返回類型相對(duì)來說是非常新的語(yǔ)法, 而且在 C 和 Java 中都沒有相似的寫法, 因此可能對(duì)讀者來說比較陌生.

在已有的代碼中有大量的函數(shù)聲明, 你不可能把它們都用新的語(yǔ)法重寫一遍. 因此實(shí)際的做法只能是使用舊的語(yǔ)法或者新舊混用. 在這種情況下, 只使用一種版本是相對(duì)來說更規(guī)整的形式.

結(jié)論

在大部分情況下, 應(yīng)當(dāng)繼續(xù)使用以往的函數(shù)聲明寫法, 即將返回類型置于函數(shù)名前. 只有在必需的時(shí)候 (如 Lambda 表達(dá)式) 或者使用后置語(yǔ)法能夠簡(jiǎn)化書寫并且提高易讀性的時(shí)候才使用新的返回類型后置語(yǔ)法. 但是后一種情況一般來說是很少見的, 大部分時(shí)候都出現(xiàn)在相當(dāng)復(fù)雜的模板代碼中, 而多數(shù)情況下不鼓勵(lì)寫這樣復(fù)雜的模板代碼.

2 命名約定

2.1 通用命名規(guī)則

盡可能使用描述性的命名双泪,不要用只有項(xiàng)目開發(fā)者才能理解的縮寫,也不能通過砍掉字母來縮寫單詞(除非是程序員都熟悉的縮寫)密似。

2.2 文件命名

總述

文件名要全部小寫,多個(gè)單詞用下劃線或者’-’連接葫盼。推薦使用””残腌。

說明

可接受的文件名命名示例:

my_useful_class.cpp
my-useful-class.cpp

不要使用已經(jīng)存在于 /usr/include 下的文件名 (Yang.Y 注: 即編譯器搜索系統(tǒng)頭文件的路徑), 如 db.h.

通常應(yīng)盡量讓文件名更加明確. http_server_logs.h就比 logs.h 要好. 定義類時(shí)文件名一般成對(duì)出現(xiàn), 如 foo_bar.h 和 foo_bar.cc, 對(duì)應(yīng)于類 FooBar.

內(nèi)聯(lián)函數(shù)必須放在 .h 文件中. 如果內(nèi)聯(lián)函數(shù)比較短, 就直接放在 .h 中.

2.3 類型命名

總述

類型名稱的每個(gè)單詞首字母均大寫, 不包含下劃線:MyExcitingClass, MyExcitingEnum.

說明

所有類型命名 —— 類, 結(jié)構(gòu)體, 類型定義 (typedef), 枚舉, 類型模板參數(shù) —— 均使用相同約定, 即以大寫字母開始, 每個(gè)單詞首字母均大寫, 不包含下劃線. 例如:

// 類和結(jié)構(gòu)體
class UrlTable { ...
class UrlTableTester { ...
struct UrlTableProperties { ...

// 類型**定義**
typedef hash_map<UrlTableProperties *, string> PropertiesMap;

// using 別名
using PropertiesMap = hash_map<UrlTableProperties *, string>;

// 枚舉
enum UrlTableErrors { ...

2.4 變量命名

總述

變量 (包括函數(shù)參數(shù)) 和數(shù)據(jù)成員名一律小寫, 單詞之間用下劃線連接. 類的成員變量以m_開始, 但結(jié)構(gòu)體的就不用, 如: a_local_variable, a_struct_data_member, m_class_data_member.

變量標(biāo)識(shí)符與對(duì)應(yīng)的類型標(biāo)識(shí)符之間的區(qū)別應(yīng)避免僅在于用小寫字母寫的初始字母不同情況。eg:Path path;是不可取的

說明

普通變量命名

舉例:

string table_name;  // 好 - 用下劃線.string tablename;   // 好 - 全小寫.

string tableName;  // 差 - 混合大小寫

類數(shù)據(jù)成員

不管是靜態(tài)還是非靜態(tài)的贫导,類數(shù)據(jù)成員都可以和普通變量一樣抛猫,但要加m或者m_前綴。

若是m前綴則m后緊跟大寫字母孩灯;若使用m_闺金,后接小寫字母。

m_用法:后接下劃線和全小寫

class TableInfo {
  ...

  private:
    string m_table_name;  // 好 - 后加下劃線.
    string m_tablename;   // 好.
    static Pool<TableInfo>* m_pool;  // 好.
};

m用法:后接首字母大寫的獨(dú)立單詞

class TableInfo {
  ...
  private:
    string mTableName;  // 好 - 后加下劃線.
    string mTableName;   // 好.
    static Pool<TableInfo>* mPool;  // 好.

};

推薦使用第一種7宓怠败匹!

結(jié)構(gòu)體變量

不管是靜態(tài)的還是非靜態(tài)的, 結(jié)構(gòu)體數(shù)據(jù)成員都可以和普通變量一樣, 不用像類成員那樣接下劃線。

struct UrlTableProperties {
    string name;
    int num_entries;
    static Pool<UrlTableProperties>* pool;
};

全局變量

總述

一般情況下禁止使用全局變量讥巡,非不得已情況下采用g_前綴掀亩,其他格式與普通變量相同。

說明

凡是需要采用全局變量extern欢顷,盡量都優(yōu)化為get槽棍,set修改內(nèi)部static靜態(tài)變量。

int g_value;

char g_name[] = {};

注:全局變量和靜態(tài)全局變量均采用此命名格式抬驴。

指針變量

總述

采用指針變量時(shí)要格外小心炼七,盡量在聲明時(shí)就初始化。避免程序中使用未初始化的野指針布持,從而導(dǎo)致程序崩潰豌拙。

說明

指針變量采用“駝峰”命名規(guī)則,即小寫p前綴鳖链、大小寫混合姆蘸、單詞首字母大寫墩莫。

char name[] = “C++ Style”;
char *pStr, *pName = name[0];
pStr = name;

2.5 常量命名

在程序代碼中,所有固定的數(shù)值(可能除了- 1,0和1)都要用常量替換逞敷,也就是說狂秦,不使用“神奇的數(shù)字”(這些數(shù)字后來都不知道這個(gè)值代表什么)常量不允許使用小寫字母,因此名稱部分之間的下劃線在這里是允許的推捐。

const double PI = 3.14
const int MAX_SIZE = 100;

注: 僅在cpp文件中使用的常量裂问,建議定義在cpp文件中以常量的形式代替宏。若是定義的常量供多個(gè)文件使用牛柒,建議以宏或者枚舉的形式在頭文件中聲明堪簿,避免編譯報(bào)重定義警告。

2.6 函數(shù)命名

總述

常規(guī)函數(shù)使用大小寫混合, 取值和設(shè)值函數(shù)則要求與變量名匹配:

MyExctingFunction();
MyExcitingMethod();
my_exciting_member_variable();
set_my_exciting_member_variable();

說明

推薦使用皮壁,函數(shù)名每個(gè)單詞首字母都需要大寫椭更,沒有下劃線。對(duì)于首字母縮寫的單詞蛾魄,更傾向于將它們視作一個(gè)單詞進(jìn)行首字母大寫 (例如, 寫作StartRpc()而非StartRPC())虑瀑。

AddTableEntry();
DeleteUrl();
OpenFileOrDie();

取值和設(shè)值函數(shù)的命名與變量一致. 一般來說它們的名稱與實(shí)際的成員變量對(duì)應(yīng), 但并不強(qiáng)制要求. 例如 int count()與void SetCount(int count)。

2.7 命名空間命名

2.8 枚舉命名

總述

枚舉的命名應(yīng)當(dāng)于常量和宏一致滴须,以大寫E字母開頭舌狗,多個(gè)單詞用下劃線_連接。ESIZE_SEARCH扔水。

說明

由于枚舉的功能與宏功能類似痛侍,故規(guī)定枚舉命名規(guī)則與宏命名一致。為避免與宏命名沖突魔市,規(guī)定枚舉命名以大寫字母E作為前綴主届,使得枚舉命名更加清晰。

enum EFormatStateExtPvr    
{
    FORMAT_STATE_PROGRESS = 0x1,
    FORMAT_STATE_NOTSET= 0xF
};

2.9 宏命名

總述

谷歌規(guī)則中推薦使用內(nèi)聯(lián)函數(shù)嘹狞、枚舉和常量代替宏的使用岂膳。

說明

宏的定義規(guī)則與枚舉一樣,只不過不需要任何字母前綴磅网,大寫以下劃線_連接谈截。

#define PI 3.14
#define MAX_SIZE 1024
#define ROUND(x) ...

宏定義中的參數(shù),可以使用小寫涧偷。

3 注釋

注釋的重要性不亞于代碼實(shí)現(xiàn)簸喂,好的注釋能夠讓代碼可讀性更強(qiáng)。優(yōu)秀的注釋能減輕開發(fā)人員自身的負(fù)擔(dān)燎潮,提高團(tuán)隊(duì)開發(fā)效率喻鳄。當(dāng)然也要注意:注釋固然重要,但最好的注釋就是代碼本身确封,有意義的類型名和變量名除呵,要遠(yuǎn)勝于用注釋解釋含糊不清的名字再菊。

3.1 注釋風(fēng)格

總述

使用// 或 /* */,都是允許的,只要統(tǒng)一即可颜曾。內(nèi)容語(yǔ)言建議使用英文纠拔。

說明

本文推薦單行注釋采用//,多行注釋采用/* */形式。若工程中已經(jīng)存在注釋風(fēng)格泛豪,需要與當(dāng)前工程保持一致即可稠诲。

3.2 文件注釋

總述

在每一個(gè)文件的開頭加入版權(quán)公告。

文件注釋應(yīng)包括版權(quán)诡曙、文件名臀叙、作者、版本价卤、描述劝萤、日志、注釋等內(nèi)容慎璧。

說明

推薦采用以下格式稳其,若工程已經(jīng)存在模板,與其他文件保持一致即可炸卑。

法律公告和作者信息

每個(gè)文件都應(yīng)該包含許可證引用. 為項(xiàng)目選擇合適的許可證版本.(比如, Apache 2.0, BSD, LGPL, GPL)

如果你對(duì)原始作者的文件做了重大修改, 請(qǐng)考慮刪除原作者信息.

文件內(nèi)容

如果一個(gè) .h 文件聲明了多個(gè)概念, 則文件注釋應(yīng)當(dāng)對(duì)文件的內(nèi)容做一個(gè)大致的說明, 同時(shí)說明各概念之間的聯(lián)系. 一個(gè)一到兩行的文件注釋就足夠了, 對(duì)于每個(gè)概念的詳細(xì)文檔應(yīng)當(dāng)放在各個(gè)概念中, 而不是文件注釋中.

/*
********************************************************************************
* Copyright (C),1975-2020, xxxxxx., Ltd.
* File Name : example.cpp
* Author :
* Version : V1.0
* Description: General driver template, if wanting to use it, you can use
* global case matching to replace DRIEVER_CASE and driver_case
* with your custom driver name.
* Journal : 2020-05-09 init v1.0 by xxxx
* Others : ********************************************************************************
*/

3.3 類注釋

總述

每個(gè)類的定義都要附帶一份注釋,除非它的功能相當(dāng)明顯煤傍。

說明

類注釋應(yīng)當(dāng)為讀者理解如何使用與何時(shí)使用類提供足夠的信息, 同時(shí)應(yīng)當(dāng)提醒讀者在正確使用此類時(shí)應(yīng)當(dāng)考慮的因素. 如果類有任何同步前提, 請(qǐng)用文檔說明. 如果該類的實(shí)例可被多線程訪問, 要特別注意文檔說明多線程環(huán)境下相關(guān)的規(guī)則和常量使用.

如果你想用一小段代碼演示這個(gè)類的基本用法或通常用法, 放在類注釋里也非常合適.

如果類的聲明和定義分開了(例如分別放在了.h和.cpp文件中), 此時(shí),描述類用法的注釋應(yīng)當(dāng)和接口定義放在一起, 描述類的操作和實(shí)現(xiàn)的注釋應(yīng)當(dāng)和實(shí)現(xiàn)放在一起.

3.4 函數(shù)注釋

總述

函數(shù)聲明處的注釋描述函數(shù)功能; 定義處的注釋描述函數(shù)實(shí)現(xiàn).

說明

函數(shù)聲明

基本上每個(gè)函數(shù)聲明處前都應(yīng)當(dāng)加上注釋, 描述函數(shù)的功能和用途. 只有在函數(shù)的功能簡(jiǎn)單而明顯時(shí)才能省略這些注釋(例如, 簡(jiǎn)單的取值和設(shè)值函數(shù)). 注釋使用敘述式 (“Opens the file”) 而非指令式 (“Open the file”); 注釋只是為了描述函數(shù), 而不是命令函數(shù)做什么. 通常, 注釋不會(huì)描述函數(shù)如何工作. 那是函數(shù)定義部分的事情.

經(jīng)常被調(diào)用的函數(shù)聲明處注釋內(nèi)容:

/*
* @description : Open the equipment
* @param - inode : transmit inode
* @param - filp : The device file, the file descriptor
* @return : 0: success; other: fail
*/
static int LedOpen(struct inode *inode, struct file *filp)
{
    if (!atomic_dec_and_test(&gpioled.lock)) {
        atomic_inc(&gpioled.lock);
        return -EBUSY;
    }
   filp->private_data = &gpioled;
   return 0;
 }

但也要避免啰嗦盖文,或者對(duì)顯而易見的內(nèi)容進(jìn)行說明. 下面的注釋就沒有必要加上 “否則返回 false”, 因?yàn)橐呀?jīng)暗含其中了:

// Returns true if the table cannot hold any more entries.
bool IsTableFull();

注釋函數(shù)重載時(shí), 注釋的重點(diǎn)應(yīng)該是函數(shù)中被重載的部分, 而不是簡(jiǎn)單的重復(fù)被重載的函數(shù)的注釋. 多數(shù)情況下, 函數(shù)重載不需要額外的文檔, 因此也沒有必要加上注釋.

注釋構(gòu)造/析構(gòu)函數(shù)時(shí), 切記讀代碼的人知道構(gòu)造/析構(gòu)函數(shù)的功能, 所以 “銷毀這一對(duì)象” 這樣的注釋是沒有意義的. 你應(yīng)當(dāng)注明的是注明構(gòu)造函數(shù)對(duì)參數(shù)做了什么 (例如, 是否取得指針?biāo)袡?quán)) 以及析構(gòu)函數(shù)清理了什么. 如果都是些無關(guān)緊要的內(nèi)容, 直接省掉注釋. 析構(gòu)函數(shù)前沒有注釋是很正常的.

函數(shù)定義****

如果函數(shù)的實(shí)現(xiàn)過程中用到了很巧妙的方式, 那么在函數(shù)定義處應(yīng)當(dāng)加上解釋性的注釋. 例如, 你所使用的編程技巧, 實(shí)現(xiàn)的大致步驟, 或解釋如此實(shí)現(xiàn)的理由. 舉個(gè)例子, 你可以說明為什么函數(shù)的前半部分要加鎖而后半部分不需要.

不要從.h文件或其他地方的函數(shù)聲明處直接復(fù)制注釋. 簡(jiǎn)要重述函數(shù)功能是可以的, 但注釋重點(diǎn)要放在如何實(shí)現(xiàn)上.

3.5 變量注釋

總述

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

說明

類數(shù)據(jù)成員

每個(gè)類數(shù)據(jù)成員 (也叫實(shí)例變量或成員變量) 都應(yīng)該用注釋說明用途. 如果有非變量的參數(shù)(例如特殊值, 數(shù)據(jù)成員之間的關(guān)系, 生命周期等)不能夠用類型與變量名明確表達(dá), 則應(yīng)當(dāng)加上注釋. 然而, 如果變量類型與變量名已經(jīng)足以描述一個(gè)變量, 那么就不再需要加上注釋.

特別地, 如果變量可以接受 NULL 或 -1 等警戒值, 須加以說明. 比如:

  private:
    /* Used to bounds-check table accesses. -1 means
       that we don't yet know how many entries the table has. */
    int m_num_total_entries;

全局變量

和數(shù)據(jù)成員一樣, 所有全局變量也要注釋說明含義及用途, 以及作為全局變量的原因. 比如:

// The total number of tests cases that we run through in this regression test.
const int g_num_testcases = 6;

3.6 實(shí)現(xiàn)注釋

總述

對(duì)于代碼中巧妙的, 晦澀的, 有趣的, 重要的地方加以注釋。

說明

代碼前注釋

/* Divide result by two, taking into account that x
   contains the carry from the add. */
    for (int i = 0; i < result->size(); i++) {
       x = (x << 8) + (*result)[i];
      (*result)[i] = x >> 1;
      x &= 1;
    }

行注釋

比較隱晦的地方要在行尾加入注釋龄恋。在行尾空兩格進(jìn)行注釋疙驾。 比如:

    // If we have enough memory, mmap the data portion too.
    mmap_budget = max<int64>(0, mmap_budget - index_->length());
    if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytes, mlock))
    return;  // Error already logged.

3.7 標(biāo)點(diǎn),拼音和語(yǔ)法

總述

注意標(biāo)點(diǎn), 拼寫和語(yǔ)法; 寫的好的注釋比差的要易讀的多郭毕。

說明

注釋的通常寫法是包含正確大小寫和結(jié)尾句號(hào)的完整敘述性語(yǔ)句. 大多數(shù)情況下, 完整的句子比句子片段可讀性更高. 短一點(diǎn)的注釋, 比如代碼行尾注釋, 可以隨意點(diǎn), 但依然要注意風(fēng)格的一致性它碎。

雖然被別人指出該用分號(hào)時(shí)卻用了逗號(hào)多少有些尷尬, 但清晰易讀的代碼還是很重要的. 正確的標(biāo)點(diǎn), 拼寫和語(yǔ)法對(duì)此會(huì)有很大幫助。

3.8 TODO注釋

總述

對(duì)那些臨時(shí)的, 短期的解決方案, 或已經(jīng)夠好但仍不完美的代碼使用 TODO 注釋.

TODO 注釋要使用全大寫的字符串 TODO, 在隨后的圓括號(hào)里寫上你的名字, 郵件地址, bug ID, 或其它身份標(biāo)識(shí)和與這一 TODO 相關(guān)的 issue. 主要目的是讓添加注釋的人 (也是可以請(qǐng)求提供更多細(xì)節(jié)的人) 可根據(jù)規(guī)范的 TODO 格式進(jìn)行查找. 添加 TODO 注釋并不意味著你要自己來修正, 因此當(dāng)你加上帶有姓名的 TODO 時(shí), 一般都是寫上自己的名字显押。

// TODO(kl@gmail.com): Use a "*" here for concatenation operator.
// TODO(Zeke) change this to use relations.
// TODO(bug 12345): remove the "Last visitors" feature

如果加 TODO 是為了在 “將來某一天做某事”, 可以附上一個(gè)非常明確的時(shí)間 “Fix by November 2005”), 或者一個(gè)明確的事項(xiàng) (“Remove this code when all clients can handle XML responses.”)扳肛。

4 格式

每個(gè)人都可能有自己的代碼風(fēng)格和格式, 但如果一個(gè)項(xiàng)目中的所有人都遵循同一風(fēng)格的話, 這個(gè)項(xiàng)目就能更順利地進(jìn)行. 每個(gè)人未必能同意下述的每一處格式規(guī)則, 而且其中的不少規(guī)則需要一定時(shí)間的適應(yīng), 但整個(gè)項(xiàng)目服從統(tǒng)一的編程風(fēng)格是很重要的, 只有這樣才能讓所有人輕松地閱讀和理解代碼。

4.1 行長(zhǎng)度

總述

每行長(zhǎng)度字符數(shù)不超過80乘碑。

盡管這條規(guī)則具有爭(zhēng)議挖息,Linux源碼的風(fēng)格也是遵照這一規(guī)則,因此一致性更重要兽肤。

優(yōu)點(diǎn)

提倡該原則的人認(rèn)為強(qiáng)迫他們調(diào)整編輯器窗口大小是很野蠻的行為. 很多人同時(shí)并排開幾個(gè)代碼窗口, 根本沒有多余的空間拉伸窗口. 大家都把窗口最大尺寸加以限定, 并且 80 列寬是傳統(tǒng)標(biāo)準(zhǔn)套腹。

缺點(diǎn)

反對(duì)該原則的人則認(rèn)為更寬的代碼行更易閱讀. 80 列的限制是上個(gè)世紀(jì) 60 年代的大型機(jī)的古板缺陷; 現(xiàn)代設(shè)備具有更寬的顯示屏, 可以很輕松地顯示更多代碼绪抛。

結(jié)論

如果無法在不傷害易讀性的條件下進(jìn)行斷行, 那么注釋行可以超過 80 個(gè)字符, 這樣可以方便復(fù)制粘貼. 例如, 帶有命令示例或 URL 的行可以超過 80 個(gè)字符。

包含長(zhǎng)路徑的 #include 語(yǔ)句可以超出80列电禀。

4.2 非ASCII字符

總述

盡量不使用非 ASCII 字符, 使用時(shí)必須使用 UTF-8 編碼幢码。

4.3 空格還是制表位

總述

只使用空格,每次縮進(jìn)4個(gè)空格鞭呕。

說明

我們使用空格縮進(jìn). 不要在代碼中使用制表符蛤育。應(yīng)該設(shè)置編輯器將制表符轉(zhuǎn)為空格。Vim中Makefile需要使用制表位葫松,Ctrl+V Tab即可出現(xiàn)制位表瓦糕。

4.4 函數(shù)聲明與定義

總述

返回類型和函數(shù)名在同一行,參數(shù)也盡量放在同一行腋么,如果放不下就對(duì)形參進(jìn)行分行咕娄,分行方式與函數(shù)調(diào)用一致。

說明

函數(shù)看上去像這樣:

ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {
    DoSomething();
    ...
}

如果同一行文本太多, 放不下所有參數(shù):

ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2,
                                             Type par_name3) {
    DoSomething();
    ...
}

甚至連第一個(gè)參數(shù)都放不下:

ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
    Type par_name1,  // 4 space indent
    Type par_name2,
    Type par_name3) {

    DoSomething();  //4space indent
    ...
}

注意以下幾點(diǎn):

  • 使用好的參數(shù)名.
  • 只有在參數(shù)未被使用或者其用途非常明顯時(shí), 才能省略參數(shù)名.
  • 如果返回類型和函數(shù)名在一行放不下, 分行.
  • 如果返回類型與函數(shù)聲明或定義分行了, 不要縮進(jìn).
  • 左圓括號(hào)總是和函數(shù)名在同一行.
  • 函數(shù)名和左圓括號(hào)間永遠(yuǎn)沒有空格.
  • 圓括號(hào)與參數(shù)間沒有空格.
  • 左大括號(hào)總在最后一個(gè)參數(shù)同一行的末尾處, 不另起新行.
  • 右大括號(hào)總是單獨(dú)位于函數(shù)最后一行, 或者與左大括號(hào)同一行.
  • 右圓括號(hào)和左大括號(hào)間總是有一個(gè)空格.
  • 所有形參應(yīng)盡可能對(duì)齊.
  • 缺省縮進(jìn)為 4 個(gè)空格.
  • 換行后的參數(shù)保持 4 個(gè)空格的縮進(jìn).

4.5 條件語(yǔ)句

總述

傾向于不在圓括號(hào)內(nèi)使用空格. 關(guān)鍵字 if 和 else 另起一行.

說明

  • if 與圓括號(hào)()與{} 都需要空格隔開
  • if (condition) { // 好 - if 和 { 都與空格緊鄰.
  • if-else后面無論包含幾行代碼珊擂,都必須緊跟{}圣勒。

4.6 循環(huán)和開關(guān)選擇語(yǔ)句

總述

循環(huán)語(yǔ)句使用{}分段。盡管很多風(fēng)格選擇switch使用{}用來表明case之間不是連在一起的摧扇,但是這里采用linux內(nèi)核風(fēng)格圣贸,不推薦case使用{}包含分支,且case位置要與switch對(duì)齊扛稽。

說明

如果有不滿足 case 條件的枚舉值, switch 應(yīng)該總是包含一個(gè) default 匹配 (如果有輸入值沒有 case 去處理, 編譯器將給出 warning). 如果 default 應(yīng)該永遠(yuǎn)執(zhí)行不到, 簡(jiǎn)單的加條 assert:

switch (var) {
case 0:     // 與switch對(duì)齊
    ...      // 4 空格縮進(jìn)
    break;
case 1:
    ...
    break;
default:
    assert(false);
    break吁峻;    
}

4.7 指針和引用表達(dá)式

總述

句點(diǎn)或箭頭前后不要有空格. 指針/地址操作符 (*, &) 之后不能有空格.

說明

下面是指針和引用表達(dá)式的正確使用范例:

x = *p;
p = &x;
x = r.y;
x = r->y;

注意:

  • 在訪問成員時(shí), 句點(diǎn)或箭頭前后沒有空格.
  • 指針操作符 * 或 & 后沒有空格.
  • 在聲明指針變量或參數(shù)時(shí), 星號(hào)與類型或變量名緊挨都可以:
// 好, 空格前置.
char *c;
const string &str;

// 好, 空格后置.
char* c;
const string& str;

int x, *y;  // 不允許 - 在多重聲明中不能使用 & 或 *
char * c;  // 差 - * 兩邊都有空格
const string & str;  // 差 - & 兩邊都有空格.

在單個(gè)文件內(nèi)要保持風(fēng)格一致, 所以, 如果是修改現(xiàn)有文件, 要遵照該文件的風(fēng)格.

4.8 布爾表達(dá)式

總述
如果一個(gè)布爾表達(dá)式超過80行, 斷行方式要統(tǒng)一一下.

說明

下例中, 邏輯與 (&&) 操作符總位于行尾:

if (this_one_thing > this_other_thing &&
    a_third_thing == a_fourth_thing &&
    yet_another && last_one) {

    ...
}

4.9 預(yù)處理指令

總述

預(yù)處理指令不要縮進(jìn), 從行首開始.

說明

即使預(yù)處理指令位于縮進(jìn)代碼塊中, 指令也應(yīng)從行首開始.

// 好 - 指令從行首開始
    if (lopsided_score) {
#if DISASTER_PENDING      // 正確 - 從行首開始
    DropEverything();
# if NOTIFY               // 非必要 - # 后跟空格
    NotifyClient();
# endif
#endif
    BackToNormal();
  }

// 差 - 指令縮進(jìn)
  if (lopsided_score) {
    #if DISASTER_PENDING  // 差 - "#if" 應(yīng)該放在行開頭
    DropEverything();
    #endif                // 差 - "#endif" 不要縮進(jìn)
    BackToNormal();
  }

4.10 類格式

總述

訪問控制塊的聲明依次序是 public:, protected:, private:, 每個(gè)都縮進(jìn) 2 個(gè)空格。

說明

類聲明的基本格式如下:

class MyClass : public OtherClass {
  public:      // 注意有一個(gè)空格的縮進(jìn)
    MyClass();  // 標(biāo)準(zhǔn)的兩空格縮進(jìn)
    explicit MyClass(int var);
    ~MyClass() {}
    void SomeFunction();
    void SomeFunctionThatDoesNothing() {
    }

    void set_some_var(int var) { some_var_ = var; }
    int some_var() const { return some_var_; }

  private:
    bool SomeInternalFunction();

    int m_some_var;
    int m_some_other_var;
};

注意事項(xiàng):

  • 所有基類名應(yīng)在 80 列限制下盡量與子類名放在同一行.
  • 關(guān)鍵詞 public:, protected:, private: 要縮進(jìn) 2 個(gè)空格.
  • 除第一個(gè)關(guān)鍵詞 (一般是 public) 外, 其他關(guān)鍵詞前要空一行. 如果類比較小的話也可以不空.
  • 這些關(guān)鍵詞后不要保留空行.
  • public 放在最前面, 然后是 protected, 最后是 private.

4.11 水平留白

總述

水平留白的使用根據(jù)在代碼中的位置決定. 永遠(yuǎn)不要在行尾添加沒意義的留白在张。

說明

通用

void f(bool b) {  // 左大括號(hào)前總是有空格.
    ...
    int i = 0;  // 分號(hào)前不加空格.
    // 列表初始化中大括號(hào)內(nèi)的空格是可選的.
    // 如果加了空格, 那么兩邊都要加上.
    int x[] = { 0 };
    int x[] = {0};
}

// 繼承與初始化列表中的冒號(hào)前后恒有空格.
class Foo : public Bar {
  public:
    /* 對(duì)于單行函數(shù)的實(shí)現(xiàn), 在大括號(hào)內(nèi)加上空格
    然后是函數(shù)實(shí)現(xiàn) */
    Foo(int b) : Bar(), baz_(b) {}  // 大括號(hào)里面是空的話, 不加空格.
    void Reset() { baz_ = 0; }  // 用括號(hào)把大括號(hào)與實(shí)現(xiàn)分開.
    ...
}

添加冗余的留白會(huì)給其他人編輯時(shí)造成額外負(fù)擔(dān). 因此, 行尾不要留空格. 如果確定一行代碼已經(jīng)修改完畢, 將多余的空格去掉; 或者在專門清理空格時(shí)去掉用含。

循環(huán)和條件語(yǔ)句

if (b) {          // if 條件語(yǔ)句和循環(huán)語(yǔ)句關(guān)鍵字后均有空格.
...
} else {          // else 前后有空格.
}while (test) {}   // 圓括號(hào)內(nèi)部不緊鄰空格.

switch (i) {
for (int i = 0; i < 5; ++i) {
switch ( i ) {    // 循環(huán)和條件語(yǔ)句的圓括號(hào)里可以與空格緊鄰.
if ( test ) {     // 圓括號(hào), 但這很少見. 總之要一致.

for ( int i = 0; i < 5; ++i ) {
    for ( ; i < 5 ; ++i) {  // 循環(huán)里內(nèi) ; 后恒有空格, ;  前可以加個(gè)空格.
        switch (i) {
        case 1:         // switch case 的冒號(hào)前無空格.
        ...
        case 2: break;  // 如果冒號(hào)有代碼, 加個(gè)空格.
        }
    }
}

操作符

// 賦值運(yùn)算符前后總是有空格.
x = 0;

// 其它二元操作符也前后恒有空格, 不過對(duì)于表達(dá)式的子式可以不加空格.
// 圓括號(hào)內(nèi)部沒有緊鄰空格.
v = w * x + y / z;
v = w*x + y/z;
v = w * (x + z);

// 在參數(shù)和一元操作符之間不加空格.
x = -5;
++x;

if (x && !y) {
    ...
}

模板和轉(zhuǎn)換

// 尖括號(hào)(< and >) 不與空格緊鄰, < 前沒有空格, > 和 ( 之間也沒有.
vector<string> x;
y = static_cast<char*>(x);

// 在類型與指針操作符之間留空格也可以, 但要保持一致.
vector<char *> x;

類數(shù)據(jù)成員與函數(shù)成員

總述

一般情況下,在類中函數(shù)成員與數(shù)據(jù)成員之間要一行留白帮匾,便于查看啄骇。

說明

class Student {
  public:
    GetStuName();
    ShowScore();

    String m_name;
    int m_score;
}

4.12 垂直留白

總述

垂直留白越少越好.

說明

這不僅僅是規(guī)則而是原則問題了: 不在萬不得已, 不要使用空行. 尤其是: 兩個(gè)函數(shù)定義之間的空行不要超過 2 行, 函數(shù)體首尾不要留空行, 函數(shù)體中也不要隨意添加空行.

基本原則是: 同一屏可以顯示的代碼越多, 越容易理解程序的控制流. 當(dāng)然, 過于密集的代碼塊和過于疏松的代碼塊同樣難看, 這取決于你的判斷. 但通常是垂直留白越少越好.

下面的規(guī)則可以讓加入的空行更有效:

  • 函數(shù)體內(nèi)開頭或結(jié)尾的空行可讀性微乎其微.
  • 在多重 if-else 塊里加空行或許有點(diǎn)可讀性.

參考

Google C++風(fēng)格指南

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瘟斜,隨后出現(xiàn)的幾起案子缸夹,更是在濱河造成了極大的恐慌,老刑警劉巖哼转,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件明未,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡壹蔓,警方通過查閱死者的電腦和手機(jī)趟妥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來佣蓉,“玉大人披摄,你說我怎么就攤上這事亲雪。” “怎么了疚膊?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵义辕,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我寓盗,道長(zhǎng)灌砖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任傀蚌,我火速辦了婚禮基显,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘善炫。我一直安慰自己撩幽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布箩艺。 她就那樣靜靜地躺著窜醉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪艺谆。 梳的紋絲不亂的頭發(fā)上榨惰,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音静汤,去河邊找鬼读串。 笑死,一個(gè)胖子當(dāng)著我的面吹牛撒妈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播排监,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼狰右,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了舆床?” 一聲冷哼從身側(cè)響起棋蚌,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挨队,沒想到半個(gè)月后谷暮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盛垦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年湿弦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腾夯。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡颊埃,死狀恐怖蔬充,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情班利,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站倦炒,受9級(jí)特大地震影響获枝,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜闯割,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一彻消、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纽谒,春花似錦证膨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至澳化,卻和暖如春崔步,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缎谷。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工井濒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人列林。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓瑞你,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親希痴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子者甲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 無規(guī)矩不成方圓。好吧…用在這里有點(diǎn)過分了砌创。簡(jiǎn)而言之虏缸,就是根據(jù)規(guī)范進(jìn)行代碼書寫,將有助于我們更好的理解與實(shí)現(xiàn)代碼與功...
    靜候那一米陽(yáng)光閱讀 1,312評(píng)論 0 2
  • 引言 C++ 是一門十分復(fù)雜并且威力強(qiáng)大的語(yǔ)言嫩实,使用這門語(yǔ)言的時(shí)候我們應(yīng)該有所節(jié)制刽辙,絕對(duì)的自由意味著混亂。 1.規(guī)...
    NHFX閱讀 543評(píng)論 0 1
  • C++ 編程規(guī)范 這是一本 由兩位世界頂級(jí)專家聯(lián)袂巨獻(xiàn)甲献,適合所有層次 C++程序員 的 C++ 界20年集大成之作...
    10d3642f94b1閱讀 258評(píng)論 0 0
  • 組織 不要拘泥于小節(jié) 在高警告級(jí)別干凈利落地進(jìn)行編譯 使用自動(dòng)構(gòu)建系統(tǒng) 適用版本控制系統(tǒng) 在代碼審查上投入 設(shè)計(jì) ...
    趙者也閱讀 468評(píng)論 0 1
  • 縮進(jìn) 采用四個(gè)空格宰缤,不要TAB 大括號(hào) 函數(shù)內(nèi)部控制語(yǔ)句左大括號(hào)不換行 變量/成員變量 小駝峰,類成員變量(靜態(tài)和...
    土豆吞噬者閱讀 996評(píng)論 0 0