C/C++高質量編程指南之一
第一章:文件結構
1 在文件開頭加上版本信息。
【規則 1-2-1】為了防止頭文件被重復引用,應當用 ifndef/define/endif 結構產生預處理塊
【規則 1-2-2】用 #include <filename.h> 格式來引用標准庫的頭文件(編譯器將從 標准庫目錄開始搜索)
【規則 1-2-3】用 #include “filename.h” 格式來引用非標准庫的頭文件(編譯器將 從用戶的工作目錄開始搜索)
【建議 1-2-1】頭文件中只存放“聲明”而不存放“定義”
第二章:程序的版式
【規則 2-1-1】在每個類聲明之后、每個函數定義結束之后都要加空行。
【規則 2-1-2】在一個函數體內,邏揖上密切相關的語句之間不加空行,其它地方應 加空行分隔。
【規則 2-2-1】一行代碼只做一件事情,如只定義一個變量,或只寫一條語句。
【規則 2-2-2】if、for、while、do 等語句自占一行,執行語句不得緊跟其后。不論 執行語句有多少都要加{}。這樣可以防止書寫失誤
【建議 2-2-1】盡可能在定義變量的同時初始化該變量(就近原則)
【規則 2-3-1】關鍵字之后要留空格。
【規則 2-3-2】函數名之后不要留空格,緊跟左括號‘(’,以與關鍵字區別
【規則 2-3-5】 “=”、“+=” “>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元 操作符的前后應當加空格
【規則 2-3-6】一元操作符如“!”、“~”、“++”、“--”、“&”(地址運算符)等前后不 加空格。
【建議 2-3-1】對於表達式比較長的 for 語句和 if 語句,為了緊湊起見可以適當地去 掉一些空格,
如 for (i=0; i<10; i++)和 if ((a<=b) && (c<=d))
【規則 2-4-1】程序的分界符‘{’和‘}’應獨占一行並且位於同一列,同時與引用 它們的語句左對齊。
【規則 2-4-2】{ }之內的代碼塊在‘{’右邊數格處左對齊。
注意:這個是有爭議的,可能只是在C/C++中這樣去做,在其他語言中好像不是這樣的,
我的習慣是第一個。
長行拆分:
【規則 2-6-1】應當將修飾符 * 和 & 緊靠變量名
char *name; int *x, y; // 此處 y 不會被誤解為指針
【規則 2-7-6】注釋的位置應與被描述的代碼相鄰,可以放在代碼的上方或右方,不 可放在下方。
第三章:命名規則
這塊好好看看哈。
【規則 3-1-3】命名規則盡量與所采用的操作系統或開發工具的風格保持一致。
例如 Windows 應用程序的標識符通常采用“大小寫”混排的方式,如 AddChild。
而 Unix 應用程序的標識符通常采用“小寫加下划線”的方式,如 add_child。
【規則 3-1-6】變量的名字應當使用“名詞”或者“形容詞+名詞” 。
float value; float oldValue; float newValue;
【規則 3-1-7】全局函數的名字應當使用“動詞”或者“動詞+名詞”(動賓詞組) 。
類的成員函數應當只使用“動詞”,被省略掉的名詞就是對象本身。
DrawBox(); // 全局函數 box->Draw(); // 類的成員函數
【建議 3-1-1】盡量避免名字中出現數字編號,如 Value1,Value2 等,除非邏輯上的 確需要編號
【規則 3-2-1】類名和函數名用大寫字母開頭的單詞組合而成。 例如:
class Node; // 類名 class LeafNode; // 類名 void Draw(void); // 函數名 void SetValue(int value); // 函數名
【規則 3-2-2】變量和參數用小寫字母開頭的單詞組合而成。
BOOL flag; int drawMode; 也可以這樣: string window_name; // OK 使用下划線 string windowname; // OK 全部小寫 string windowName; // Bad 大小寫混合使用 全局變量:沒有特殊要求,盡量少用?可以加上前綴g_以與局部變量區分。 類的成員變量:可以加上前綴m_ 當然也有就加一個_的
【規則 3-2-3】常量全用大寫的字母,用下划線分割單詞
const int MAX = 100; const int MAX_LENGTH = 100;
【規則 3-2-4】靜態變量加前綴 s_(表示 static)
static int s_initValue; // 靜態變量
【規則 3-2-5】如果不得已需要全局變量,則使全局變量加前綴 g_(表示 global)。
int g_howMuchMoney; // 全局變量
【規則 3-2-6】類的數據成員加前綴 m_(表示 member),這樣可以避免數據成員與 成員函數的參數同名。
第四章:表達式和基本語句
【規則 4-1-1】如果代碼行中的運算符比較多,用括號確定表達式的操作順序,避免 使用默認的優先級。
與零值比較:
布爾變量與零值比較
【規則 4-3-1】不可將布爾變量直接與 TRUE、FALSE 或者 1、0 進行比較。
假設布爾變量名字為 flag,它與零值比較的標准 if 語句如下:
if (flag) // 表示 flag 為真
if (!flag) // 表示 flag 為假
整型變量與零值比較 :
【規則 4-3-2】應當將整型變量用“==”或“!=”直接與 0 比較。
if (value == 0)
if (value != 0)
浮點變量與零值比較 :
【規則 4-3-3】不可將浮點變量用“==”或“!=”與任何數字比較。
千萬要留意,無論是 float 還是 double 類型的變量,都有精度限制。
所以一定要避 免將浮點變量用“==”或“!=”與數字比較,應該設法轉化成“>=”或“<=”形式。
正確的比較方式
if ((x>=-EPSINON) && (x<=EPSINON)) 其中 EPSINON 是允許的誤差(即精度) 。
其中EPSINON是允許的誤差(即精度)。 const float EPSINON = 0.000001,至於為什么取0.000001,可以自己按實際情況定義。
指針變量與零值比較
【規則 4-3-4】應當將指針變量用“==”或“!=”與 NULL 比較
if (p == NULL) // p 與 NULL 顯式比較,強調 p 是指針變量
if (p != NULL)
【建議 4-4-1】在多重循環中,如果有可能,應當將長的循環放在內層,短的 循環放在外層,以減少 CPU 跨切循環層的次數
第五章:常量
為什么需要常量:
如果不使用常量,直接在程序中填寫數字或字符串,將會有什么麻煩?
(1) 程序的可讀性(可理解性)變差。程序員自己會忘記那些數字或字符串是什么意 思,用戶則更加不知它們從何處來、表示什么。
(2) 在程序的很多地方輸入同樣的數字或字符串,難保不發生書寫錯誤。
(3) 如果要修改數字或字符串,則會在很多地方改動,既麻煩又容易出錯。
【規則 5-1-1】 盡量使用含義直觀的常量來表示那些將在程序中多次出現的數字或
#define MAX 100 /* C 語言的宏常量 */ const int MAX = 100; // C++ 語言的 const 常量 const float PI = 3.14159; // C++ 語言的 const 常量
const和define的比較
C++ 語言可以用 const 來定義常量,也可以用 #define 來定義常量。但是前者比后 者有更多的優點:
(1) const 常量有數據類型,而宏常量沒有數據類型。編譯器可以對前者進行類型安 全檢查。而對后者只進行字符替換,沒有類型安全檢查,並且在字符替換可能會 產生意料不到的錯誤(邊際效應)。
(2) 有些集成化的調試工具可以對 const常量進行調試,但是不能對宏常量進行調試。
【規則 5-2-1】在 C++ 程序中只使用 const 常量而不使用宏常量,即 const 常量完 全取代宏常量。
【規則 5-3-1】需要對外公開的常量放在頭文件中,不需要對外公開的常量放在定義 文件的頭部。為便於管理,可以把不同模塊的常量集中存放在一個公共的頭文件中。
第六章:函數設計
【規則 6-1-2】參數命名要恰當,順序要合理。
應將目的參數放在前面,源參數放在后面。
例如編寫字符串拷貝函數 StringCopy
void StringCopy(char *strDestination,const char *strSource);
調用時:StringCopy(str, “Hello World”);
【規則 6-1-4】如果輸入參數以值傳遞的方式傳遞對象,則宜改用“const &”方式來 傳遞,這樣可以省去臨時對象的構造和析構過程,從而提高效率
【建議 6-2-1】有時候函數原本不需要返回值,但為了增加靈活性如支持鏈式表達,
例如字符串拷貝函數 strcpy 的原型:
char *strcpy(char *strDest,const char *strSrc);
適用於:int length = strlen( strcpy(str, “Hello World”) );
【規則 6-3-1】在函數體的“入口處”,對參數的有效性進行檢查 用assert
【規則 6-3-2】在函數體的“出口處”,對 return 語句的正確性和效率進行檢查。
(1)return 語句不可返回指向“棧內存”的“指針”或者“引用”,因為該內存在函數 體結束時被自動銷毀。
例如
char * Func(void) { char str[] = “hello world”; // str 的內存位於棧上 … return str; // 將導致錯誤 }
(2)要搞清楚返回的究竟是“值” 、“指針”還是“引用”
(3)如果函數返回值是一個對象,要考慮 return 語句的效率。
例如 return String(s1 + s2);
這是臨時對象的語法,表示“創建一個臨時對象並返回它”。
不要以為它與“先創建 一個局部對象 temp 並返回它的結果”是等價的,
如:
String temp(s1 + s2);
return temp; 。
實質不然,上述代碼將發生三件事。
首先,temp 對象被創建,同時完成初始化;
然 后拷貝構造函數把 temp 拷貝到保存返回值的外部存儲單元中;
最后,temp 在函數結束 時被銷毀(調用析構函數)。
然而“創建一個臨時對象並返回它”的過程是不同的,編譯 器直接把臨時對象創建並初始化在外部存儲單元中,省去了拷貝和析構的化費,提高了 效率。
第七章到最后