天天看點

《C++覆轍錄》——常見錯誤1:過分積極的注釋

本節書摘來自異步社群出版社《c++覆轍錄》一書中的第1章,第1節,作者: 【美】stephen c. dewhurst(史蒂芬 c. 杜赫斯特),更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

c++覆轍錄

說一個問題是基礎的,并不就是說它不是嚴重的或不是普遍存在的。事實上,本章所讨論的基礎問題的共同特點比起在以後章節讨論的技術複雜度而言,可能更側重于使人警醒。這裡讨論的問題,由于它們的基礎性,在某種程度上可以說它們普遍存在于幾乎所有的c++代碼中。

很多注釋都是畫蛇添足,它們隻會讓源代碼更難讀,更難維護,并經常把維護工程師引入歧途。考慮下面的簡單語句:

<code>a = b; // 将b指派給a</code>

這個注釋難道比代碼本身更能說明這個語句的意義嗎?因而它是完全無用的。事實上,它比完全無用還要壞。它是害人精。首先,這條注釋轉移了代碼閱讀者的注意力,增加了閱讀量因而使代碼更費解。其次,要維護的東西更多了,因為注釋也是要随着它描述的代碼的更改而更改的。最後,這個對注釋的更改常常會被遺忘1:

<code>c = b; // 将b指派給a</code>

仔細的維護工程師不會武斷地說注釋是錯的2,是以他就被迫要去檢視整個程式以确定到底是注釋錯了呢,還是有意為之呢(c可能是a的引用),還是本來正确隻是比較間接的呢(指派給c可能引發一些傳播效應以使a的值也發生相應變化),等等,總之這一行就根本不應該帶注釋。

<code>a = b;</code>

還是這代碼本來的樣子最清楚地表明了其意義,也沒有額外的注釋需要維護。這在精神上也符合老生常談,亦即“最有效率的代碼就是根本不存在的代碼”。這條經驗對于注釋也适用:最好的注釋就是根本用不着寫的注釋,因為要注釋的代碼已經“自注釋”了。

另一些常見的非必要的注釋的例子經常可以在型别的定義裡見到,它們要麼是病态的編碼标準的怪胎,要麼就是出自c++新手:

class c {

// 公開接口

protected:

  c( int ); // 預設構造函數

public:

  virtual~c(); // 析構函數

  // ...

};<code>`</code>

軟體工程師還有一種強烈的心理趨勢就是盡量不要“平白無故”地在源檔案文本中多寫哪怕一行。這裡公布一個有趣的本行業秘密:如果某種結構(函數啦、型别的公開接口啦什麼的)能被塞在一“頁”裡,也就在三四十行左右3的話,它就很容易了解。假如有些内容跑到第二頁去了,它了解起來就難了一倍。如果三頁才塞得下,據估計了解難度就成原來的4倍了4。一種特别聲名狼藉的編碼實踐就是把更改日志作為注釋插入到源檔案的頭部或尾部:

<code>/* 6/17/02 scd把一個該死的bug幹掉了 */</code>

這到底是有用的資訊,抑或是僅僅是維護工程師的自吹自擂?在這行注釋被寫下以後的一兩個星期,它怎麼看也不再像是有用的了,但它卻也許要在代碼裡粘上很多年,欺騙着一批又一批的維護工程師。最好是用你的版本控制軟體來做這種無用注釋真正想做的事,c++的源代碼檔案裡可沒有閑地方來放這些勞什子。

想不用注釋卻又要使代碼意義明确、容易維護的最好辦法就是遵循簡單易行的、定義良好的命名習慣來為你使用的實體(函數、型别、變量等)取個清晰的、反映其抽象含義的名字。(函數)聲明中形參的名字尤其重要。考慮一個帶有3個同一型别引數的函數:

void perform( int actioncode, int source, int destination);①<code>`</code>

①譯者注:這很明顯是herb sutter倡導的命名規則(原諒我又多寫了一行注釋)。

這就好多了。按理,我們還需要寫一行注釋來說明這個函數的用途(而不是如何實作的)。形參的一個最引人入勝之處就是,不像注釋,它們是随着餘下的代碼一起更改的,即使改了也不影響代碼的意義。話雖然這麼說,但我不能想像任何一個軟體工程師在引數意義改變了的時候,會不給它取個新名字5。但我能舉出一串軟體工程師來,他們改了代碼但老是忘記維護注釋。

kathy stark在programming in c++中說得好:“如果在程式裡用意義明确、脫口而出的名字,那麼注釋隻是偶爾才需要。如果不用意義明确的名字,即使加上了注釋也不能讓代碼更好懂一些。”

另一種最大程度地減少注釋書寫的辦法是采用标準庫中的或人盡皆知的元件:

`

printf( "hello, world!" ); // 在螢幕上列印“hello, world” `

上面這個注釋不但是無用的,而且隻在部分情況下正确。标準庫元件不僅是“自注釋”的,并且有關它們的文檔汗牛充棟,有口皆碑。

繼續閱讀