天天看點

代碼風格(3)——注釋一、總述二、檔案注釋三、類注釋四、函數注釋五、變量注釋六、實作注釋七、TODO注釋

一、總述

注釋雖然寫起來很痛苦,但對保證代碼可讀性至關重要。

當然也要記住:注釋固然很重要, 但最好的代碼應當本身就是文檔。有意義的類型名和變量名,要遠勝過要用注釋解釋的含糊不清的名字。

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. 注釋

繼續閱讀