7. 注釋

2018-02-24 15:11 更新

注釋雖然寫起來很痛苦, 但對保證代碼可讀性至關(guān)重要. 下面的規(guī)則描述了如何注釋以及在哪兒注釋. 當(dāng)然也要記住: 注釋固然很重要, 但最好的代碼本身應(yīng)該是自文檔化. 有意義的類型名和變量名, 要遠勝過要用注釋解釋的含糊不清的名字.

你寫的注釋是給代碼讀者看的: 下一個需要理解你的代碼的人. 慷慨些吧, 下一個人可能就是你!

7.1. 注釋風(fēng)格

Tip

使用 ///* */, 統(tǒng)一就好.

///* */ 都可以; 但 // 常用. 要在如何注釋及注釋風(fēng)格上確保統(tǒng)一.

7.2. 文件注釋

Tip

在每一個文件開頭加入版權(quán)公告, 然后是文件內(nèi)容描述.

法律公告和作者信息:
每個文件都應(yīng)該包含以下項, 依次是:

  • 版權(quán)聲明 (比如, Copyright 2008 Google Inc.)
  • 許可證. 為項目選擇合適的許可證版本 (比如, Apache 2.0, BSD, LGPL, GPL)
  • 作者: 標(biāo)識文件的原始作者.

如果你對原始作者的文件做了重大修改, 將你的信息添加到作者信息里. 這樣當(dāng)其他人對該文件有疑問時可以知道該聯(lián)系誰.

文件內(nèi)容:
緊接著版權(quán)許可和作者信息之后, 每個文件都要用注釋描述文件內(nèi)容.

通常, .h 文件要對所聲明的類的功能和用法作簡單說明. .cc 文件通常包含了更多的實現(xiàn)細節(jié)或算法技巧討論, 如果你感覺這些實現(xiàn)細節(jié)或算法技巧討論對于理解 .h 文件有幫助, 可以該注釋挪到 .h, 并在 .cc 中指出文檔在 .h.

不要簡單的在 .h.cc 間復(fù)制注釋. 這種偏離了注釋的實際意義.

7.3. 類注釋

Tip

每個類的定義都要附帶一份注釋, 描述類的功能和用法.

// 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 {
    ...
};

如果你覺得已經(jīng)在文件頂部詳細描述了該類, 想直接簡單的來上一句 “完整描述見文件頂部” 也不打緊, 但務(wù)必確保有這類注釋.

如果類有任何同步前提, 文檔說明之. 如果該類的實例可被多線程訪問, 要特別注意文檔說明多線程環(huán)境下相關(guān)的規(guī)則和常量使用.

7.4. 函數(shù)注釋

Tip

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

函數(shù)聲明:
注釋位于聲明之前, 對函數(shù)功能及用法進行描述. 注釋使用敘述式 (“Opens the file”) 而非指令式 (“Open the file”); 注釋只是為了描述函數(shù), 而不是命令函數(shù)做什么. 通常, 注釋不會描述函數(shù)如何工作. 那是函數(shù)定義部分的事情.

函數(shù)聲明處注釋的內(nèi)容:

  • 函數(shù)的輸入輸出.
  • 對類成員函數(shù)而言: 函數(shù)調(diào)用期間對象是否需要保持引用參數(shù), 是否會釋放這些參數(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;

但也要避免羅羅嗦嗦, 或做些顯而易見的說明. 下面的注釋就沒有必要加上 “returns false otherwise”, 因為已經(jīng)暗含其中了:

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

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

函數(shù)定義:
每個函數(shù)定義時要用注釋說明函數(shù)功能和實現(xiàn)要點. 比如說說你用的編程技巧, 實現(xiàn)的大致步驟, 或解釋如此實現(xiàn)的理由, 為什么前半部分要加鎖而后半部分不需要.

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

7.5. 變量注釋

Tip

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

類數(shù)據(jù)成員:
每個類數(shù)據(jù)成員 (也叫實例變量或成員變量) 都應(yīng)該用注釋說明用途. 如果變量可以接受 NULL-1 等警戒值, 須加以說明. 比如:

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ù)成員一樣, 所有全局變量也要注釋說明含義及用途. 比如:

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

7.6. 實現(xiàn)注釋

Tip

對于代碼中巧妙的, 晦澀的, 有趣的, 重要的地方加以注釋.

代碼前注釋:
巧妙或復(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;
}

行注釋:
比較隱晦的地方要在行尾加入注釋. 在行尾空兩格進行注釋. 比如:

// 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ù)返回時錯誤已經(jīng)被記入日志.

如果你需要連續(xù)進行多行注釋, 可以使之對齊獲得更好的可讀性:

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ù)時, 要注釋說明含義, 或使用常量讓代碼望文知意. 例如, 對比:

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);

不允許:
注意 永遠不要 用自然語言翻譯代碼作為注釋. 要假設(shè)讀代碼的人 C++ 水平比你高, 即便他/她可能不知道你的用意:

Warning

// 現(xiàn)在, 檢查 b 數(shù)組并確保 i 是否存在,
// 下一個元素是 i+1.
...        // 天哪. 令人崩潰的注釋.

7.7. 標(biāo)點, 拼寫和語法

Tip

注意標(biāo)點, 拼寫和語法; 寫的好的注釋比差的要易讀的多.

注釋的通常寫法是包含正確大小寫和結(jié)尾句號的完整語句. 短一點的注釋 (如代碼行尾注釋) 可以隨意點, 依然要注意風(fēng)格的一致性. 完整的語句可讀性更好, 也可以說明該注釋是完整的, 而不是一些不成熟的想法.

雖然被別人指出該用分號時卻用了逗號多少有些尷尬, 但清晰易讀的代碼還是很重要的. 正確的標(biāo)點, 拼寫和語法對此會有所幫助.

7.8. TODO 注釋

Tip

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

TODO 注釋要使用全大寫的字符串 TODO, 在隨后的圓括號里寫上你的大名, 郵件地址, 或其它身份標(biāo)識. 冒號是可選的. 主要目的是讓添加注釋的人 (也是可以請求提供更多細節(jié)的人) 可根據(jù)規(guī)范的 TODO 格式進行查找. 添加 TODO 注釋并不意味著你要自己來修正.

// TODO(kl@gmail.com): Use a "*" here for concatenation operator.
// TODO(Zeke) change this to use relations.

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

譯者 (YuleFox) 筆記

  1. 關(guān)于注釋風(fēng)格,很多 C++ 的 coders 更喜歡行注釋, C coders 或許對塊注釋依然情有獨鐘, 或者在文件頭大段大段的注釋時使用塊注釋;
  2. 文件注釋可以炫耀你的成就, 也是為了捅了簍子別人可以找你;
  3. 注釋要言簡意賅, 不要拖沓冗余, 復(fù)雜的東西簡單化和簡單的東西復(fù)雜化都是要被鄙視的;
  4. 對于 Chinese coders 來說, 用英文注釋還是用中文注釋, it is a problem, 但不管怎樣, 注釋是為了讓別人看懂, 難道是為了炫耀編程語言之外的你的母語或外語水平嗎;
  5. 注釋不要太亂, 適當(dāng)?shù)目s進才會讓人樂意看. 但也沒有必要規(guī)定注釋從第幾列開始 (我自己寫代碼的時候總喜歡這樣), UNIX/LINUX 下還可以約定是使用 tab 還是 space, 個人傾向于 space;
  6. TODO 很不錯, 有時候, 注釋確實是為了標(biāo)記一些未完成的或完成的不盡如人意的地方, 這樣一搜索, 就知道還有哪些活要干, 日志都省了.
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號