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

2021-09-23 16:47:58 字數 2490 閱讀 2629

c++覆轍錄

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

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

a = b; // 將b賦值給a

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

c = b; // 將b賦值給a

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

a = b;

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

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

class c ;```

你會覺得別人在挑戰你的智商。要是某個維護工程師連「public:」是什麼意思都需要教,你還敢讓他碰你的**嗎?對於任何有經驗的c++軟體工程師而言,這些注釋除了給**添亂、增加需要維護的文字數量以外沒有任何用處:

class c ;`

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

/* 6/17/02 scd把乙個該死的bug乾掉了 */

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

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

/*

從源到目的執行乙個動作

第乙個引數是動作編碼(action code),第二個引數是源(source),第三個引數是目的(destination)

*/void perform( int, int, int );```

這也不算太壞吧,不過如果引數是七八個而不是3個你又該寫多少東西呢?我們明明可以做得更好:

void perform( int actioncode, int source, int destination);①`

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

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

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

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

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

上面這個注釋不但是無用的,而且只在部分情況下正確。標準庫元件不僅是「自注釋」的,並且有關它們的文件汗牛充棟,有口皆碑。

swap( a, a+1 ); 

sort( a, a+max );

copy( a, a+max, ostream_iterator(cout,"\n") );```

因為swap、sort和copy都是標準庫元件,對它們加上任何注釋都是成心添亂,而且給定義得好好的標準操作規格描述帶來了(非必要的)不確定性。

《C 覆轍錄》 1 3 全域性變數

很難找到任何理由去硬生生地宣告什麼全域性變數。全域性變數阻礙了 重用,而且使 變得更難維護。它們阻礙重用是因為任何使用了全域性變數的 就立刻與之耦合,這使得全域性變數一改它們也非得跟著改,從而使任何重用都不可能了。它們使 變得更難維護的原因是很難甄別出哪些 用了某個特定的全域性變數,因為任何 都有訪...

c語言常見錯誤1

字元變數用scanf賦值前不加getchar 如下 include int main int num char ch scanf d num scanf c ch printf num d num printf ch c ch return 0 執行時會發現程式直接跳過了ch的輸入,這是因為當整數賦...

《C 覆轍錄》 1 11 聰明反被聰明誤

c 語言和c語言看起來會吸引相當多的人去張揚個性 你有沒有聽說過乙個叫 obfuscated eiffel 的比賽?46。在這些軟體工程師的思維裡,兩點間的最短距離是普通歐氏空間之球面扭曲上的大圓。試舉一例 在c 語言的圈子裡 且不論這個圈子是不是普通歐氏空間裡的 的排版格式純粹是為了方便解讀 的人...