1. 程式人生 > >高質量C++/C 程式設計指南

高質量C++/C 程式設計指南

1.2 標頭檔案的結構

【規則1-2-1】為了防止標頭檔案被重複引用,應當用ifndef/define/endif 結構產生預處理塊。

【規則1-2-2】用 #include <filename.h> 格式來引用標準庫的標頭檔案(編譯器將從標準庫目錄開始搜尋)。

【規則1-2-3】用 #include “filename.h” 格式來引用非標準庫的標頭檔案(編譯器將從使用者的工作目錄開始搜尋)。

【建議1-2-1】標頭檔案中只存放“宣告”而不存放“定義”

【建議1-2-2】不提倡使用全域性變數,儘量不要在標頭檔案中出現象extern int value 這類宣告。

2.1 空行

【規則2-1-1】在每個類宣告之後、每個函式定義結束之後都要加空行。

【規則2-1-2】在一個函式體內,邏揖上密切相關的語句之間不加空行,其它地方應加空行分隔。

2.2 程式碼行

【規則2-2-1】一行程式碼只做一件事情,如只定義一個變數,或只寫一條語句。這樣的程式碼容易閱讀,並且方便於寫註釋。

【規則2-2-2】if、for、while、do 等語句自佔一行,執行語句不得緊跟其後。不論執行語句有多少都要加{}。這樣可以防止書寫失誤。

【建議2-2-1】儘可能在定義變數的同時初始化該變數(就近原則)

2.3 程式碼行內的空格

【規則2-3-1】關鍵字之後要留空格。象const、virtual、inline、case 等關鍵字之後至少要留一個空格,否則無法辨析關鍵字。象if、for、while 等關鍵字之後應留
一個空格再跟左括號‘(’,以突出關鍵字。

【規則2-3-2】函式名之後不要留空格,緊跟左括號‘(’,以與關鍵字區別。

【規則2-3-3】‘(’向後緊跟,‘)’、‘,’、‘;’向前緊跟,緊跟處不留空格。

【規則2-3-4】‘,’之後要留空格,如Function(x, y, z)。如果‘;’不是一行的結束符號,其後要留空格,如for (initialization; condition; update)。

【規則2-3-5】賦值操作符、比較操作符、算術操作符、邏輯操作符、位域操作符,如“=”、“+=” “>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前後應當加空格。

【規則2-3-6】一元操作符如“!”、“~”、“++”、“--”、“&”(地址運算子)等前後不加空格。

【規則2-3-7】象“[]”、“.”、“->”這類操作符前後不加空格。

【建議2-3-1】對於表示式比較長的for 語句和if 語句,為了緊湊起見可以適當地去掉一些空格,如for (i=0; i<10; i++)和if ((a<=b) && (c<=d))

2.4 對齊

【規則2-4-1】程式的分界符‘{’和‘}’應獨佔一行並且位於同一列,同時與引用它們的語句左對齊。

【規則2-4-2】{ }之內的程式碼塊在‘{’右邊數格處左對齊。

2.5 長行拆分

【規則2-5-1】程式碼行最大長度宜控制在70 至80 個字元以內。程式碼行不要過長,否則眼睛看不過來,也不便於列印。

【規則2-5-2】長表示式要在低優先順序操作符處拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要進行適當的縮排,使排版整齊,語句可讀。

2.6 修飾符的位置

【規則2-6-1】應當將修飾符 * 和 & 緊靠變數名

2.7 註釋

【規則2-7-1】註釋是對程式碼的“提示”,而不是文件。程式中的註釋不可喧賓奪主,註釋太多了會讓人眼花繚亂。註釋的花樣要少。

【規則2-7-2】如果程式碼本來就是清楚的,則不必加註釋。否則多此一舉,令人厭煩。

【規則2-7-3】邊寫程式碼邊註釋,修改程式碼同時修改相應的註釋,以保證註釋與程式碼的一致性。不再有用的註釋要刪除。

【規則2-7-4】註釋應當準確、易懂,防止註釋有二義性。錯誤的註釋不但無益反而有害。

【規則2-7-5】儘量避免在註釋中使用縮寫,特別是不常用縮寫。

【規則2-7-6】註釋的位置應與被描述的程式碼相鄰,可以放在程式碼的上方或右方,不可放在下方。

【規則2-7-8】當代碼比較長,特別是有多重巢狀時,應當在一些段落的結束處加註釋,便於閱讀。

3.1 共性規則

【規則3-1-1】識別符號應當直觀且可以拼讀,可望文知意,不必進行“解碼”。

【規則3-1-2】識別符號的長度應當符合“min-length && max-information”原則。

【規則3-1-3】命名規則儘量與所採用的作業系統或開發工具的風格保持一致。

【規則3-1-4】程式中不要出現僅靠大小寫區分的相似的識別符號。

【建議3-1-1】儘量避免名字中出現數字編號,如Value1,Value2 等,除非邏輯上的確需要編號。這是為了防止程式設計師偷懶,不肯為命名動腦筋而導致產生無意義的名字(因為用數字編號最省事)。

3.2 簡單的Windows 應用程式命名規則

【規則3-2-1】類名和函式名用大寫字母開頭的單詞組合而成。

【規則3-2-2】變數和引數用小寫字母開頭的單詞組合而成。

【規則3-2-3】常量全用大寫的字母,用下劃線分割單詞。

【規則3-2-3】常量全用大寫的字母,用下劃線分割單詞。

【規則3-2-4】靜態變數加字首s_(表示static)。

【規則3-2-5】如果不得已需要全域性變數,則使全域性變數加字首g_(表示global)。

【規則3-2-6】類的資料成員加字首m_(表示member),這樣可以避免資料成員與成員函式的引數同名。

【規則3-2-7】為了防止某一軟體庫中的一些識別符號和其它軟體庫中的衝突,可以為各種識別符號加上能反映軟體性質的字首。例如三維圖形標準OpenGL 的所有庫函式均以gl 開頭,所有常量(或巨集定義)均以GL 開頭。

4.1 運算子的優先順序

【規則4-1-1】如果程式碼行中的運算子比較多,用括號確定表示式的操作順序,避免使用預設的優先順序。

4.2 複合表示式

【規則4-2-1】不要編寫太複雜的複合表示式。

【規則4-2-2】不要有多用途的複合表示式。

【規則4-2-3】不要把程式中的複合表示式與“真正的數學表示式”混淆。

4.3 if 語句

【規則4-3-1】不可將布林變數直接與TRUE、FALSE 或者1、0 進行比較。

【規則4-3-2】應當將整型變數用“==”或“!=”直接與0 比較。

【規則4-3-3】不可將浮點變數用“==”或“!=”與任何數字比較。

【規則4-3-4】應當將指標變數用“==”或“!=”與NULL 比較。

4.4 迴圈語句的效率

【建議4-4-1】在多重迴圈中,如果有可能,應當將最長的迴圈放在最內層,最短的迴圈放在最外層,以減少CPU 跨切迴圈層的次數。

【建議4-4-2】如果迴圈體記憶體在邏輯判斷,並且迴圈次數很大,宜將邏輯判斷移到迴圈體的外面

4.5 for 語句的迴圈控制變數

【規則4-5-1】不可在for 迴圈體內修改迴圈變數,防止for 迴圈失去控制。

【建議4-5-1】建議for 語句的迴圈控制變數的取值採用“半開半閉區間”寫法。

4.6 switch 語句

【規則4-6-1】每個case 語句的結尾不要忘了加break,否則將導致多個分支重疊(除非有意使多個分支重疊)。

【規則4-6-2】不要忘記最後那個default 分支。即使程式真的不需要default 處理,也應該保留語句 default : break; 這樣做並非多此一舉,而是為了防止別人誤以為你忘了default 處理。

5.1 為什麼需要常量

【規則5-1-1】 儘量使用含義直觀的常量來表示那些將在程式中多次出現的數字或字串。

5.2 const 與 #define 的比較

【規則5-2-1】在C++ 程式中只使用const 常量而不使用巨集常量,即const 常量完全取代巨集常量。

5.3 常量定義規則

【規則5-3-1】需要對外公開的常量放在標頭檔案中,不需要對外公開的常量放在定義檔案的頭部。為便於管理,可以把不同模組的常量集中存放在一個公共的標頭檔案中。

【規則5-3-2】如果某一常量與其它常量密切相關,應在定義中包含這種關係,而不應給出一些孤立的值。

6.1 引數的規則

【規則6-1-1】引數的書寫要完整,不要貪圖省事只寫引數的型別而省略引數名字。如果函式沒有引數,則用void 填充。

【規則6-1-2】引數命名要恰當,順序要合理。

【規則6-1-3】如果引數是指標,且僅作輸入用,則應在型別前加const,以防止該指標在函式體內被意外修改。例如:void StringCopy(char *strDestination,const char *strSource);

【規則6-1-4】如果輸入引數以值傳遞的方式傳遞物件,則宜改用“const &”方式來傳遞,這樣可以省去臨時物件的構造和析構過程,從而提高效率。

【建議6-1-1】避免函式有太多的引數,引數個數儘量控制在5 個以內。如果引數太多,在使用時容易將引數型別或順序搞錯。

【建議6-1-2】儘量不要使用型別和數目不確定的引數。C 標準庫函式printf 是採用不確定引數的典型代表,其原型為:int printf(const chat *format[, argument]...); 這種風格的函式在編譯時喪失了嚴格的型別安全檢查。

6.2 返回值的規則

【規則6-2-1】不要省略返回值的型別。

【規則6-2-2】函式名字與返回值型別在語義上不可衝突。

【規則6-2-3】不要將正常值和錯誤標誌混在一起返回。正常值用輸出引數獲得,而錯誤標誌用return 語句返回。

【建議6-2-1】有時候函式原本不需要返回值,但為了增加靈活性如支援鏈式表達,可以附加返回值。

【建議6-2-2】如果函式的返回值是一個物件,有些場合用“引用傳遞”替換“值傳遞”可以提高效率。而有些場合只能用“值傳遞”而不能用“引用傳遞”,否則會出錯。

6.3 函式內部實現的規則

【規則6-3-1】在函式體的“入口處”,對引數的有效性進行檢查。

【規則6-3-2】在函式體的“出口處”,對return 語句的正確性和效率進行檢查。

6.4 其它建議

【建議6-4-1】函式的功能要單一,不要設計多用途的函式。

【建議6-4-2】函式體的規模要小,儘量控制在50 行程式碼之內。

【建議6-4-3】儘量避免函式帶有“記憶”功能。相同的輸入應當產生相同的輸出。建議儘量少用static 區域性變數,除非必需。

【建議6-4-4】不僅要檢查輸入引數的有效性,還要檢查通過其它途徑進入函式體內的變數的有效性,例如全域性變數、檔案控制代碼等。

【建議6-4-5】用於出錯處理的返回值一定要清楚,讓使用者不容易忽視或誤解錯誤情況。

6.5 使用斷言

【規則6-5-1】使用斷言捕捉不應該發生的非法情況。不要混淆非法情況與錯誤情況之間的區別,後者是必然存在的並且是一定要作出處理的。

【規則6-5-2】在函式的入口處,使用斷言檢查引數的有效性(合法性)。

【建議6-5-1】在編寫函式時,要進行反覆的考查,並且自問:“我打算做哪些假定?”一旦確定了的假定,就要使用斷言對假定進行檢查。

【建議6-5-2】一般教科書都鼓勵程式設計師們進行防錯設計,但要記住這種程式設計風格可能會隱瞞錯誤。當進行防錯設計時,如果“不可能發生”的事情的確發生了,則要使用斷言進行報警。

7.2 常見的記憶體錯誤及其對策

【規則7-2-1】用malloc 或new 申請記憶體之後,應該立即檢查指標值是否為NULL。防止使用指標值為NULL 的記憶體。

【規則7-2-2】不要忘記為陣列和動態記憶體賦初值。防止將未被初始化的記憶體作為右值使用。

【規則7-2-3】避免陣列或指標的下標越界,特別要當心發生“多1”或者“少1”操作。

【規則7-2-4】動態記憶體的申請與釋放必須配對,防止記憶體洩漏。

【規則7-2-5】用free 或delete 釋放了記憶體之後,立即將指標設定為NULL,防止產生“野指標”。

8.3 引數的預設值

【規則8-3-1】引數預設值只能出現在函式的宣告中,而不能出現在定義體中。

【規則8-3-2】如果函式有多個引數,引數只能從後向前挨個兒預設,否則將導致函式呼叫語句怪模怪樣。

10.1 繼承

【規則10-1-1】如果類A 和類B 毫不相關,不可以為了使B 的功能更多些而讓B繼承A 的功能和屬性。不要覺得“白吃白不吃”,讓一個好端端的健壯青年無緣無故地吃人蔘補身體。

【規則10-1-2】若在邏輯上B 是A 的“一種”(a kind of ),則允許B 繼承A 的功能和屬性。例如男人(Man)是人(Human)的一種,男孩(Boy)是男人的一種。那麼類Man 可以從類Human 派生,類Boy 可以從類Man 派生。嚴格的繼承規則應當是:若在邏輯上B 是A 的“一種”,並且A 的所有功能和屬性對B 而言都有意義,則允許B 繼承A 的功能和屬性。

10.2 組合

【規則10-2-1】若在邏輯上A 是B 的“一部分”(a part of),則不允許B 從A 派生,而是要用A 和其它東西組合出B。

11.2 提高程式的效率

【規則11-2-1】不要一味地追求程式的效率,應當在滿足正確性、可靠性、健壯性、可讀性等質量因素的前提下,設法提高程式的效率。

【規則11-2-2】以提高程式的全域性效率為主,提高區域性效率為輔。

【規則11-2-3】在優化程式的效率時,應當先找出限制效率的“瓶頸”,不要在無關緊要之處優化。

【規則11-2-4】先優化資料結構和演算法,再優化執行程式碼。

【規則11-2-5】有時候時間效率和空間效率可能對立,此時應當分析那個更重要,作出適當的折衷。例如多花費一些記憶體來提高效能。

【規則11-2-6】不要追求緊湊的程式碼,因為緊湊的程式碼並不能產生高效的機器碼。

【建議11-3-1】當心那些視覺上不易分辨的操作符發生書寫錯誤。我們經常會把“==”誤寫成“=”,象“||”、“&&”、“<=”、“>=”這類符號也很容易發生“丟1”失誤。然而編譯器卻不一定能自動指出這類錯誤。

【建議11-3-2】變數(指標、陣列)被建立之後應當及時把它們初始化,以防止把未被初始化的變數當成右值使用。

【建議11-3-3】當心變數的初值、預設值錯誤,或者精度不夠。

【建議11-3-4】當心資料型別轉換髮生錯誤。儘量使用顯式的資料型別轉換(讓人們知道發生了什麼事),避免讓編譯器輕悄悄地進行隱式的資料型別轉換。

【建議11-3-5】當心變數發生上溢或下溢,陣列的下標越界。

【建議11-3-6】當心忘記編寫錯誤處理程式,當心錯誤處理程式本身有誤。

【建議11-3-7】當心檔案I/O 有錯誤。

【建議11-3-8】避免編寫技巧性很高程式碼。

【建議11-3-9】不要設計面面俱到、非常靈活的資料結構。

【建議11-3-10】如果原有的程式碼質量比較好,儘量複用它。但是不要修補很差勁的程式碼,應當重新編寫。

【建議11-3-11】儘量使用標準庫函式,不要“發明”已經存在的庫函式。

【建議11-3-12】儘量不要使用與具體硬體或軟體環境關係密切的變數。

【建議11-3-13】把編譯器的選擇項設定為最嚴格狀態。

【建議11-3-14】如果可能的話,使用PC-Lint、LogiScope 等工具進行程式碼審查。 [最後修改由 rik, 於 2005-05-14 14:34:35]