一、總述
注釋雖然寫起來很痛苦,但對保證代碼可讀性至關重要。
當然也要記住:注釋固然很重要, 但最好的代碼應當本身就是文檔。有意義的類型名和變量名,要遠勝過要用注釋解釋的含糊不清的名字。
1.1 注釋風格
使用
//
或
都可以;但
//
更常用
二、檔案注釋
在每一個檔案開頭加入版權公告
/**===========================================================================
Copyright (C) XXXX All rights reserved.
@file user_udp.c
@brief 本檔案用于UDP通訊接口
@author Leung Man-Wah
@version r0.1
@date 2019/07/04
@license XXXX
----------------------------------------------------------------------------
Remark: (備注描述)
----------------------------------------------------------------------------
History
----------------------------------------------------------------------------
<Date> | <Version> | <Author> | <Description>
-------------|-----------|----------------|---------------------------------
2019/07/04 | r0.1 | Leung Man-Wah | 建立
-------------|-----------|----------------|---------------------------------
| | |
-------------|-----------|----------------|---------------------------------
| | |
-------------|-----------|----------------|---------------------------------
| | |
============================================================================*/
2.1 法律公告和作者資訊
每個檔案都應該包含許可證引用。為項目選擇合适的許可證版本。(比如, Apache 2.0, BSD, LGPL, GPL)
如果你對原始作者的檔案做了重大修改,請考慮删除原作者資訊。
2.2 檔案内容
如果一個
.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
{
...
};
類注釋應當為讀者了解如何使用與何時使用類提供足夠的資訊,同時應當提醒讀者在正确使用此類時應當考慮的因素。如果類有任何同步前提,請用文檔說明。如果該類的執行個體可被多線程通路,要特别注意文檔說明多線程環境下相關的規則和常量使用。
如果你想用一小段代碼示範這個類的基本用法或通常用法,放在類注釋裡也非常合适。
如果類的聲明和定義分開了(例如分别放在了
.h
和
.cc
檔案中),此時,描述類用法的注釋應當和接口定義放在一起,描述類的操作和實作的注釋應當和實作放在一起。
四、函數注釋
基本上每個函數定義處前都應當加上注釋,描述函數的功能和用途。隻有在函數的功能簡單而明顯時才能省略這些注釋(例如,簡單的取值和設值函數)。
函數聲明處注釋的内容:
- 函數的輸入輸出。
- 對類成員函數而言:函數調用期間對象是否需要保持引用參數, 是否會釋放這些參數。
- 函數是否配置設定了必須由調用者釋放的空間。
- 參數是否可以為空指針。
- 是否存在函數使用上的性能隐患。
- 如果函數是可重入的,其同步前提是什麼?
/**
@brief UDP發送繼電器狀态
@param deviceType -[in] 裝置類型
@param pMacAddr -[in] MAC位址
@param relayStatus -[in] 繼電器狀态
@return 無
*/
void UdpSendRelayStatus(uint8 deviceType, uint8 *pMacAddr, uint8 relayStatus)
{
}
/**
@brief JSON格式封裝裝置資訊
@param pSendData -[in&out] 要封裝的發送資料
@return 無
*/
static void jsonPackageDeviceInfo(char *pSendData)
{
}
注釋函數重載時,注釋的重點應該是函數中被重載的部分,而不是簡單的重複被重載的函數的注釋。多數情況下,函數重載不需要額外的文檔,是以也沒有必要加上注釋。
注釋構造/析構函數時,切記讀代碼的人知道構造/析構函數的功能,是以 “銷毀這一對象” 這樣的注釋是沒有意義的。你應當注明的是注明構造函數對參數做了什麼 (例如,是否取得指針所有權) 以及析構函數清理了什麼。如果都是些無關緊要的内容,直接省掉注釋。析構函數前沒有注釋是很正常的。
五、變量注釋
通常變量名本身足以很好說明變量用途。某些情況下,也需要額外的注釋說明。
5.1 類資料成員
每個類資料成員 (也叫執行個體變量或成員變量) 都應該用注釋說明用途。如果有非變量的參數(例如特殊值,資料成員之間的關系,生命周期等)不能夠用類型與變量名明确表達,則應當加上注釋。然而,如果變量類型與變量名已經足以描述一個變量,那麼就不再需要加上注釋。
特别地,如果變量可以接受
NULL
或
-1
等警戒值,須加以說明。比如:
private:
// Used to bounds-check table accesses. -1 means
// that we don't yet know how many entries the table has.
int numTotalEntries;
5.2 全局變量
和資料成員一樣,所有全局變量也要注釋說明含義及用途,以及作為全局變量的原因。比如:
// The total number of tests cases that we run through in this regression test.
int g_numTestCases = 6;
六、實作注釋
對于代碼中巧妙的,晦澀的,有趣的,重要的地方加以注釋。
6.1 代碼前注釋
巧妙或複雜的代碼段前要加注釋,比如:
// 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;
}
6.2 行注釋
比較隐晦的地方要在行尾加入注釋,在行尾空兩格進行注釋。比如:
// 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.
}
注意,這裡用了兩段注釋分别描述這段代碼的作用,和提示函數傳回時錯誤已經被記入日志。
如果你需要連續進行多行注釋,可以使之對齊獲得更好的可讀性:
DoSomething(); // Comment here so the comments line up.
DoSomethingElseThatIsLonger(); // 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.
}
std::vector<string> list
{
// Comments in braced lists describe the next element...
"First item", // .. and should be aligned appropriately.
"Second item"
};
DoSomething(); // For trailing block comments, one space is fine.
七、TODO注釋
對那些臨時的,短期的解決方案,或已經夠好但仍不完美的代碼使用
TODO
注釋。
TODO
注釋要使用全大寫的字元串
TODO
,在随後的圓括号裡寫上你的名字,郵件位址, bug ID,或其它身份辨別和與這一
TODO
相關的 issue。主要目的是讓添加注釋的人 (也是可以請求提供更多細節的人) 可根據規範的
TODO
格式進行查找。添加
TODO
注釋并不意味着你要自己來修正,是以當你加上帶有姓名的
TODO
時, 一般都是寫上自己的名字。
// TODO([email protected]): Use a "*" here for concatenation operator.
// TODO(Zeke) change this to use relations.
// TODO(bug 12345): remove the "Last visitors" feature
如果加
TODO
是為了在 “将來某一天做某事”,可以附上一個非常明确的時間 “Fix by November 2005”),或者一個明确的事項 (“Remove this code when all clients can handle XML responses.”)。
TODO
很不錯,有時候,注釋确實是為了标記一些未完成的或完成的不盡如人意的地方,這樣一搜尋,就知道還有哪些活要幹,日志都省了。
• 由 Leung 寫于 2019 年 10 月 28 日
• 參考:Google 開源項目風格指南——8. 注釋