注釋雖然寫起來很痛苦,但對保證代碼可讀性至關(guān)重要。下面的規(guī)則描述了如何注釋以及在哪兒注釋。當(dāng)然也要記住:注釋固然很重要,但最好的代碼本身應(yīng)該是自文檔化的。有意義的類型名和變量名,要遠勝過要用注釋解釋的含糊不清的名字。
你寫的注釋是給下一個需要理解你的代碼的人看的。慷慨些吧,下一個人可能就是你!
使用//或者/**/,統(tǒng)一就好。
//或者/**/都可以,但是//更常用。要在如何注釋以及注釋風(fēng)格上確保統(tǒng)一。
在每一個文件開頭加入版權(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)細(xì)節(jié)或算法技巧討論,如果你感覺這些實現(xiàn)細(xì)節(jié)或者算法技巧討論對于理解.h文件有幫助,可以將該注釋挪到.h中,并且在.cc中指出文檔在.h中。不要簡單的在.h和.cc間復(fù)制注釋。這種偏離了注釋的實際意義。每個類的定義都要附帶一份注釋,描述類的功能和用法。
// 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)在文件頂部詳細(xì)描述了該類,像直接簡單的上來一句“完整描述見文件頂部”也不打緊,但務(wù)必確保有這類注釋。
如果類有任何同步前提,文檔說明之。如果該類的實例可被多線程訪問,要特別注意文檔說明多線程環(huán)境下相關(guān)的規(guī)則和常量使用。
函數(shù)聲明處注釋描述函數(shù)的功能;定義處描述函數(shù)實現(xiàn)。
函數(shù)聲明:
注釋位于聲明之前,對函數(shù)功能及用法進行描述。注釋使用敘述式(”O(jiān)pens the file”)而非指令式(”O(jiān)pen 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;上面給出了一個具體示例。但是也要避免啰啰嗦嗦,或做些顯而易見的說明。注釋構(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)上。通常變量名本身足以很好地說明變量用途。但在某些情況下,也需要額外的注釋說明。
類數(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_total_entries_;全局變量:
和數(shù)據(jù)成員一樣,所有全局變量也要注釋說明含義以及用途。比如:// The total number of tests cases that we run through in this regression test.const int kNumTestCases = 6;對于代碼中巧妙的,晦澀的,有趣的,重要的地方加以注釋。
代碼前注釋:
巧妙或者復(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;}行注釋:
比較晦澀的地方要在行尾加入注釋。在行尾空兩格進行注釋,比如下面的代碼示例。注意這里用了兩段注釋分別描述這段代碼的作用,和提示函數(shù)返回時錯誤已經(jīng)被記錄入日志。// 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.如果你需要連續(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.}向函數(shù)傳入NULL,布爾值或者整數(shù)時,要注釋說明含義,或使用常量讓代碼望文知義。例如對比:// First version:bool success = CalculateSomething(interesting_value, 10, false, NULL); // What are these arguments??// Second version:bool success = CalculateSomething(interesting_value, 10, // Default base value. false, // Not the first time we're calling this.// Third version:const int kDefaultBaseValue = 10;const bool kFirstTimeCalling = false;Callback *null_callback = NULL;bool success = CalculateSomething(interesting_value, kDefaultBaseValue, kFirstTimeCalling, null_callback);不允許: - 注意永遠不要用自然語言翻譯代碼作為注釋。要假設(shè)讀代碼的人C++水平比你高,即使他/她可能不知道你的用意:
// Bad styles:// 現(xiàn)在, 檢查 b 數(shù)組并確保 i 是否存在,// 下一個元素是 i+1.... // 天哪. 令人崩潰的注釋.注意標(biāo)點,拼寫和語法;寫的好的注釋比差的要易讀的多。
注釋的通常寫法是包含正確的大小寫和結(jié)尾句號的完整語句。短一點的注釋(如代碼行尾注釋)可以隨意點,但依然要注意風(fēng)格的一致性。完整的語句可讀性更好,也可以說明該注釋是完整的,而不是一些不成熟的想法。
雖然被別人指出該用分號時卻用了逗號多少有些尷尬,但清晰易讀的代碼還是很重要的。正確的標(biāo)點,拼寫和語法對此會有幫助。
對那些臨時的,短期的解決方案,或已經(jīng)夠好但仍不完美的代碼使用TODO注釋。
TODO
注釋要使用全大寫的字符串TODO
,在隨后的圓括弧里面寫上你的大名,郵件地址,或其它身份標(biāo)識。冒號是可選的。主要目的是讓添加注釋的人(也是可以請求提供更多細(xì)節(jié)的人)可以根據(jù)規(guī)范的TODO
格式進行查找。添加注釋并不意味著你要自己來修正。
如果TODO
是為了在“將來某一天做某事”,可以附上一個非常明確的時間“Fix by November 2005”或者一個明確的事項(例如“Remove this code when all clients can handle xml responses.”)。
通過棄用注釋(DEPRECATED comments)以標(biāo)記某接口點(interface points)已棄用。
您可以寫上包含全大寫的DEPRECATED的注釋,以標(biāo)記某接口為棄用狀態(tài)。注釋可以放在接口聲明前,或者同一行。
在DEPRECATED一詞后,留下您的名字,郵箱地址以及括弧補充。
僅僅標(biāo)記接口為DEPRECATED并不會讓大家不約而同的棄用,您還得親自主動修正調(diào)用點(callsites),或者是找個幫手。
修正好的代碼應(yīng)該不會再設(shè)計棄用接口點了,著實改用新接口點。如果您不知從何下手,可以找標(biāo)記棄用注釋的當(dāng)事人一起商量。
新聞熱點
疑難解答
圖片精選