注釋雖然寫(xiě)起來(lái)很痛苦, 但對(duì)保證代碼可讀性至關(guān)重要. 下面的規(guī)則描述了如何注釋以及在哪兒注釋. 當(dāng)然也要記住: 注釋固然很重要, 但最好的代碼本身應(yīng)該是自文檔化. 有意義的類(lèi)型名和變量名, 要遠(yuǎn)勝過(guò)要用注釋解釋的含糊不清的名字.
你寫(xiě)的注釋是給代碼讀者看的: 下一個(gè)需要理解你的代碼的人. 慷慨些吧, 下一個(gè)人可能就是你!
Tip
使用
//
或/* */
, 統(tǒng)一就好.
//
或 /* */
都可以; 但 //
更 常用. 要在如何注釋及注釋風(fēng)格上確保統(tǒng)一.
Tip
在每一個(gè)文件開(kāi)頭加入版權(quán)公告, 然后是文件內(nèi)容描述.
法律公告和作者信息:
每個(gè)文件都應(yīng)該包含以下項(xiàng), 依次是:
- 版權(quán)聲明 (比如,
Copyright 2008 Google Inc.
)- 許可證. 為項(xiàng)目選擇合適的許可證版本 (比如, Apache 2.0, BSD, LGPL, GPL)
- 作者: 標(biāo)識(shí)文件的原始作者.
如果你對(duì)原始作者的文件做了重大修改, 將你的信息添加到作者信息里. 這樣當(dāng)其他人對(duì)該文件有疑問(wèn)時(shí)可以知道該聯(lián)系誰(shuí).
文件內(nèi)容:
緊接著版權(quán)許可和作者信息之后, 每個(gè)文件都要用注釋描述文件內(nèi)容.
通常, .h
文件要對(duì)所聲明的類(lèi)的功能和用法作簡(jiǎn)單說(shuō)明. .cc
文件通常包含了更多的實(shí)現(xiàn)細(xì)節(jié)或算法技巧討論, 如果你感覺(jué)這些實(shí)現(xiàn)細(xì)節(jié)或算法技巧討論對(duì)于理解 .h
文件有幫助, 可以該注釋挪到 .h
, 并在 .cc
中指出文檔在 .h
.
不要簡(jiǎn)單的在 .h
和 .cc
間復(fù)制注釋. 這種偏離了注釋的實(shí)際意義.
Tip
每個(gè)類(lèi)的定義都要附帶一份注釋, 描述類(lèi)的功能和用法.
// Iterates over the contents of a GargantuanTable. Sample usage:
// GargantuanTable_Iterator* iter = table->NewIterator();
// for (iter->Seek("foo"); !iter->done(); iter->Next()) {
// process(iter->key(), iter->value());
// }
// delete iter;
class GargantuanTable_Iterator {
...
};
如果你覺(jué)得已經(jīng)在文件頂部詳細(xì)描述了該類(lèi), 想直接簡(jiǎn)單的來(lái)上一句 “完整描述見(jiàn)文件頂部” 也不打緊, 但務(wù)必確保有這類(lèi)注釋.
如果類(lèi)有任何同步前提, 文檔說(shuō)明之. 如果該類(lèi)的實(shí)例可被多線(xiàn)程訪問(wèn), 要特別注意文檔說(shuō)明多線(xiàn)程環(huán)境下相關(guān)的規(guī)則和常量使用.
Tip
函數(shù)聲明處注釋描述函數(shù)功能; 定義處描述函數(shù)實(shí)現(xiàn).
函數(shù)聲明:
注釋位于聲明之前, 對(duì)函數(shù)功能及用法進(jìn)行描述. 注釋使用敘述式 (“Opens the file”) 而非指令式 (“Open the file”); 注釋只是為了描述函數(shù), 而不是命令函數(shù)做什么. 通常, 注釋不會(huì)描述函數(shù)如何工作. 那是函數(shù)定義部分的事情.
函數(shù)聲明處注釋的內(nèi)容:
- 函數(shù)的輸入輸出.
- 對(duì)類(lèi)成員函數(shù)而言: 函數(shù)調(diào)用期間對(duì)象是否需要保持引用參數(shù), 是否會(huì)釋放這些參數(shù).
- 如果函數(shù)分配了空間, 需要由調(diào)用者釋放.
- 參數(shù)是否可以為
NULL
.- 是否存在函數(shù)使用上的性能隱患.
- 如果函數(shù)是可重入的, 其同步前提是什么?
舉例如下:
// 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;
但也要避免羅羅嗦嗦, 或做些顯而易見(jiàn)的說(shuō)明. 下面的注釋就沒(méi)有必要加上 “returns false otherwise”, 因?yàn)橐呀?jīng)暗含其中了:
// Returns true if the table cannot hold any more entries.
bool IsTableFull();
注釋構(gòu)造/析構(gòu)函數(shù)時(shí), 切記讀代碼的人知道構(gòu)造/析構(gòu)函數(shù)是干啥的, 所以 “destroys this object” 這樣的注釋是沒(méi)有意義的. 注明構(gòu)造函數(shù)對(duì)參數(shù)做了什么 (例如, 是否取得指針?biāo)袡?quán)) 以及析構(gòu)函數(shù)清理了什么. 如果都是些無(wú)關(guān)緊要的內(nèi)容, 直接省掉注釋. 析構(gòu)函數(shù)前沒(méi)有注釋是很正常的.
函數(shù)定義:
每個(gè)函數(shù)定義時(shí)要用注釋說(shuō)明函數(shù)功能和實(shí)現(xiàn)要點(diǎn). 比如說(shuō)說(shuō)你用的編程技巧, 實(shí)現(xiàn)的大致步驟, 或解釋如此實(shí)現(xiàn)的理由, 為什么前半部分要加鎖而后半部分不需要.
不要 從 .h
文件或其他地方的函數(shù)聲明處直接復(fù)制注釋. 簡(jiǎn)要重述函數(shù)功能是可以的, 但注釋重點(diǎn)要放在如何實(shí)現(xiàn)上.
Tip
通常變量名本身足以很好說(shuō)明變量用途. 某些情況下, 也需要額外的注釋說(shuō)明.
類(lèi)數(shù)據(jù)成員:
每個(gè)類(lèi)數(shù)據(jù)成員 (也叫實(shí)例變量或成員變量) 都應(yīng)該用注釋說(shuō)明用途. 如果變量可以接受 NULL
或 -1
等警戒值, 須加以說(shuō)明. 比如:
private:
// Keeps track of the total number of entries in the table.
// Used to ensure we do not go over the limit. -1 means
// that we don't yet know how many entries the table has.
int num_totalentries;
全局變量:
和數(shù)據(jù)成員一樣, 所有全局變量也要注釋說(shuō)明含義及用途. 比如:
// The total number of tests cases that we run through in this regression test.
const int kNumTestCases = 6;
Tip
對(duì)于代碼中巧妙的, 晦澀的, 有趣的, 重要的地方加以注釋.
代碼前注釋:
巧妙或復(fù)雜的代碼段前要加注釋. 比如:
// 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(0, mmapbudget - index->length());
if (mmap_budget >= datasize && !MmapData(mmap_chunk_bytes, mlock))
return; // Error already logged.
注意, 這里用了兩段注釋分別描述這段代碼的作用, 和提示函數(shù)返回時(shí)錯(cuò)誤已經(jīng)被記入日志.
如果你需要連續(xù)進(jìn)行多行注釋, 可以使之對(duì)齊獲得更好的可讀性:
DoSomething(); // Comment here so the comments line up.
DoSomethingElseThatIsLonger(); // Comment here so there are two spaces between
// the code and the comment.
{ // One space before comment when opening a new scope is allowed,
// thus the comment lines up with the following comments and code.
DoSomethingElse(); // Two spaces before line comments normally.
}
NULL, true/false, 1, 2, 3...:
向函數(shù)傳入 NULL
, 布爾值或整數(shù)時(shí), 要注釋說(shuō)明含義, 或使用常量讓代碼望文知意. 例如, 對(duì)比:
Warning
bool success = CalculateSomething(interesting_value,
10,
false,
NULL); // What are these arguments??
和:
bool success = CalculateSomething(interesting_value,
10, // Default base value.
false, // Not the first time we're calling this.
NULL); // No callback.
或使用常量或描述性變量:
const int kDefaultBaseValue = 10;
const bool kFirstTimeCalling = false;
Callback *null_callback = NULL;
bool success = CalculateSomething(interesting_value,
kDefaultBaseValue,
kFirstTimeCalling,
null_callback);
不允許:
注意 永遠(yuǎn)不要 用自然語(yǔ)言翻譯代碼作為注釋. 要假設(shè)讀代碼的人 C++ 水平比你高, 即便他/她可能不知道你的用意:
Warning
// 現(xiàn)在, 檢查 b 數(shù)組并確保 i 是否存在,
// 下一個(gè)元素是 i+1.
... // 天哪. 令人崩潰的注釋.
Tip
注意標(biāo)點(diǎn), 拼寫(xiě)和語(yǔ)法; 寫(xiě)的好的注釋比差的要易讀的多.
注釋的通常寫(xiě)法是包含正確大小寫(xiě)和結(jié)尾句號(hào)的完整語(yǔ)句. 短一點(diǎn)的注釋 (如代碼行尾注釋) 可以隨意點(diǎn), 依然要注意風(fēng)格的一致性. 完整的語(yǔ)句可讀性更好, 也可以說(shuō)明該注釋是完整的, 而不是一些不成熟的想法.
雖然被別人指出該用分號(hào)時(shí)卻用了逗號(hào)多少有些尷尬, 但清晰易讀的代碼還是很重要的. 正確的標(biāo)點(diǎn), 拼寫(xiě)和語(yǔ)法對(duì)此會(huì)有所幫助.
Tip
對(duì)那些臨時(shí)的, 短期的解決方案, 或已經(jīng)夠好但仍不完美的代碼使用
TODO
注釋.
TODO
注釋要使用全大寫(xiě)的字符串 TODO
, 在隨后的圓括號(hào)里寫(xiě)上你的大名, 郵件地址, 或其它身份標(biāo)識(shí). 冒號(hào)是可選的. 主要目的是讓添加注釋的人 (也是可以請(qǐng)求提供更多細(xì)節(jié)的人) 可根據(jù)規(guī)范的 TODO
格式進(jìn)行查找. 添加 TODO
注釋并不意味著你要自己來(lái)修正.
// TODO(kl@gmail.com): Use a "*" here for concatenation operator.
// TODO(Zeke) change this to use relations.
如果加 TODO
是為了在 “將來(lái)某一天做某事”, 可以附上一個(gè)非常明確的時(shí)間 “Fix by November 2005”), 或者一個(gè)明確的事項(xiàng) (“Remove this code when all clients can handle XML responses.”).
更多建議: