查爾斯.普雷斯特.斯科特(Charles Prestwich Scott):
評論是自由的,但事實是神圣的
注釋可以將優(yōu)秀的代碼和糟糕的代碼區(qū)分開來,將艱澀難懂的邏輯與清晰友好的算法區(qū)分開。但我們也無須過分夸大注釋的作用。如果說你的代碼是蛋糕,那么你的注釋就如同蛋糕上糖衣,它被精巧地涂抹以增加蛋糕的美感和價值,但不能掩飾蛋糕的裂縫和瑕疵。
一、什么是代碼注釋
- 從語法角度看,注釋是編譯器將忽略不計的源代碼。
- 從語義角度看,注釋是對其所處位置的代碼的注解。
注釋的作用有多種:
1) 強調(diào)某個特殊的問題領(lǐng)域;
2)在頭文件中記錄媒介;
3)描述某個算法,協(xié)助維護程序;
4)分割各個函數(shù)以幫組快速瀏覽整個源文件。 - 注釋是內(nèi)部的文檔化機制
二、注釋標(biāo)記
- C語言的注
/*開頭,*/結(jié)束,可以跨多行。 - C++、C99、C#和Java語言增加了以
//作為開頭的單行注釋。 - 其他語言也提供了類似的塊注釋和行注釋功能,語法可能不同。
三、多少注釋是恰當(dāng)?shù)?/h3>
小威廉.斯特倫克(William Strunk Jr.):
鏗鏘有力之文必簡潔
- 把重點放在注釋的質(zhì)量上,而不是數(shù)量上;比編寫多少注釋更重要的是注釋的內(nèi)容。
- 只在能為代碼增加色彩時才編寫注釋。優(yōu)秀的演奏家追求用最少的演奏創(chuàng)造出最美的聲音。
- 真正好的代碼并不需要注釋,它們能自我解釋。像
f()和g()這樣的函數(shù)名才會大聲嚷嚷,要求注釋說明它們,但是像someGoodExample()這樣的函數(shù)名根本不需要注釋。
四、注釋中應(yīng)該有什么
賀瑞斯(Horace):
寫好源頭和本源是明智的做法
小威廉.斯特倫克(William Strunk Jr.):
鏗鏘有力之文必簡潔
f()和g()這樣的函數(shù)名才會大聲嚷嚷,要求注釋說明它們,但是像someGoodExample()這樣的函數(shù)名根本不需要注釋。賀瑞斯(Horace):
寫好源頭和本源是明智的做法
不好的注釋比沒有注釋更糟糕,它們歪曲事實進而誤導(dǎo)讀者。
解釋為什么,而不是怎么樣
(a).注釋不是用來描述程序是怎樣運行的,代碼自己是其最權(quán)威的描述。
(b).注釋應(yīng)該描述代碼塊起什么作用。
寫/* update WidgetList structure from GibWLRegistry*/這樣的注釋,不如寫/* cache widget information for later*/。注釋同一段代碼,后者表達(dá)代碼目的,而前者只告訴你這段代碼做了什么。
(c).注釋可用來解釋為什么代碼要這么寫。如有兩種可供選擇的實現(xiàn)方式,你決定采用其中一種,也許你該使用注釋來解釋一下,為什么你選擇了這種實現(xiàn)方式。不要取代或重復(fù)代碼
(a).如果注釋描述的內(nèi)容可以由編程語言本身實現(xiàn),那么就試著用具體語句表達(dá)它。
(b).如果你發(fā)現(xiàn)你用大量注釋解釋某個算法如何運行,請趕快停止,然后考慮是否需要重構(gòu)代碼或算法。
(c).不要用注釋描述變量的用法,重命名變量。
(d).如果你要文檔化一個應(yīng)當(dāng)總是成立的條件,也許你該寫一個斷言確保注釋有用
注釋的作用是標(biāo)注和解釋代碼。注釋需要滿足:
(a).記錄意想不到的內(nèi)容。如果代碼有一部分是不常見、意想不到的,用注釋記錄下來;如果有特定問題需要規(guī)避,如一個操作系統(tǒng)問題,應(yīng)該在注釋中說明。
(b).講真話。不要寫不準(zhǔn)確的注釋。
(c).清晰明了。不要讓注釋模棱兩可,內(nèi)容盡量具體;不一定是完整的、語法正確的句子,但必須是可讀的;避免縮寫。
(d).只關(guān)注當(dāng)前代碼,避免分心。不要記錄過去我們怎么做;不要把應(yīng)當(dāng)剔除的代碼(如舊代碼)包含在注釋中;避免使用ASCII藝術(shù);不要在代碼塊結(jié)尾添加多余的注釋,如在if語句的后括號后注釋//end if。
每當(dāng)寫完注釋后,在代碼上下文中回顧一遍這些注釋,考慮一下:它們是否都是正確的信息,它們是否簡潔,它們是否容易理解。
五、實踐
看看下面一小段C++代碼:
for (int i = 0; i < wlst.sz(); ++i)
k(wlst[i])
很明顯,你根本搞不懂這是在干嗎。應(yīng)用合理排版規(guī)則和添加注釋說明對它進行改進:
// Iterate over all widgets in the widget list
for (int i = 0; i < wlst.sz(); ++i)
{
// print out this widget
k(wlst[i]);
}
現(xiàn)在好多了,起碼這段代碼的目的清晰了。但它還是不太令人滿足。使用恰當(dāng)?shù)暮瘮?shù)名和變量重寫代碼:
for (int i = 0; i < widgets.size(); ++i)
printWidget(widgets[i]);
再看代碼,發(fā)現(xiàn)根本不需要注釋,代碼本身能自我描述了。
這印證了:
不要文檔化差勁的代碼——重寫這些代碼
六、注釋的一些細(xì)節(jié)及其作用
根據(jù)你自己的品味,把下面的建議當(dāng)作指導(dǎo)性的原則:
一致性。
所有的注釋應(yīng)該清晰明了,前后一致。為你的注釋選擇一種特定的布局方式,始終堅持使用。清晰的塊注釋
將首位標(biāo)記(如C/C++中/*和*/)各自放在一行,凸顯它們;讓塊注釋左側(cè)多縮進一個字符,讓注釋顯的像一個整體。如
/*
* This is
* a block
* comment.
*/
而不是這種
/*
This is
a block
comment.
*/
-
縮進的注釋
注釋不應(yīng)該截斷代碼或打亂邏輯流程。讓注釋與對應(yīng)的代碼的縮進一樣。
看到下面這樣的代碼,無疑更費勁:
void strangeCommentStyle()
{
for (int i = 0; i < JUST_ENOUGH; ++i)
{
//This is a meaning comment about the next line.
doSomethingMeaningful(i);
// good comment
doOtherOperation(i);
}
}
在不帶括號的循環(huán)中,不要把注釋放在單循環(huán)體語句之前,這會導(dǎo)致災(zāi)難性后果。
-
行尾注釋
大多數(shù)注釋都是放在各自的行上,但有時較短的單行注釋可以跟在代碼語句后頭。這種情況下,在注釋與代碼之間留白是一種好習(xí)慣。例如:
Class Point
{
public:
........
private:
int x; //X軸坐標(biāo)值
int y; //Y軸坐標(biāo)值
int z; //Z軸坐標(biāo)值
};
如果行尾注釋直接跟在聲明后面,代碼看起來會顯得擁擠,讀起來更費勁。
-
選擇一種維護成本較低的風(fēng)格
可以把更多時間放在編寫代碼而不是擺弄注釋上。
-
幫組閱讀代碼
a).注釋通常在它描述的代碼上方,而不是下方。讀者一般都是從上向下閱讀,這樣注釋可以讓讀者為即將讀到的內(nèi)容做好準(zhǔn)備。
b).注釋后面緊跟代碼,代碼后面放一個空白行,接下來是下一個注釋代碼段。這種代碼與空白一起使用的方式,可以將代碼分隔成"段落",益于閱讀。
-
分隔板
注釋經(jīng)常被用作不同代碼部分之間的“分隔板”。如在一個若干個類或函數(shù)的源文件中,可能有以下注釋:
/******************************************
* class foo implementation
******************************************/
或
////////////////////////////////////////////////////
我們應(yīng)當(dāng)避免大量使用這種"分隔板"類的注釋。將代碼分組的應(yīng)該是良好的縮進和結(jié)構(gòu),而不是生動的ASCII藝術(shù)。
標(biāo)志
注釋還可以用作代碼中內(nèi)嵌的標(biāo)志。如:
a).//XXX用來標(biāo)識棘手的代碼或需要重新編寫的代碼;
b).//FIXME表示已知已經(jīng)損壞的代碼;
c).//TODO用來標(biāo)識缺少一些功能,需要日后返工。文件頭注釋
a).所有源文件都應(yīng)該以描述其內(nèi)容的注釋塊作為開頭。這個頭注釋是一個概述前言,提供了文件的關(guān)鍵信息。
b).頭注釋應(yīng)該包含文件的目的、版本、所有權(quán)及版權(quán)聲明;
c).頭注釋不需要把文件中定義的所有函數(shù)、類、全局變量等內(nèi)容羅列,太累贅。幫組編寫程序
a).一種常見的編碼方式是首先用注釋構(gòu)造代碼結(jié)構(gòu),然后在各行注釋下面填寫代碼。這種方式要注意及時刪除一些無用的注釋和修改一些不恰當(dāng)?shù)淖⑨尅?br> b).另一種常見的編碼方式是徒手編寫代碼,再添加注釋??赡艹3M浤稠椆ぷ骰虿荒軐懗龊玫淖⑨尅8玫姆绞绞沁厡懘a邊寫注釋,實踐會告訴你寫多少注釋。注釋過時
要明白注釋會過時。當(dāng)你修正、添加或修改任何代碼時,請同時修正、添加或修改相應(yīng)的注釋。
八、總結(jié)
德爾莫.施瓦茨(Delmore Schwartz):
重要的寫作是要講眼之所見,這樣就不必一遍又一遍的口口相傳了
寫高質(zhì)量的注釋比寫高數(shù)量的注釋更好,然而,最好的是寫出高質(zhì)量的代碼——那些無需注釋的自文檔化代碼。