《高質量C/C++程式設計指南》學習筆記

Hao_09發表於2015-03-26
這本書只有短短100頁,可是卻可以稱得上是膾炙人口。一年前初讀此書時就感覺受益良多,而今再次重讀,仍然不肯停頓一口氣讀完。針對書中指出的一些程式設計規則和建議,摘錄下個人認為比較好的部分。

第1章 檔案結構

為了防止標頭檔案被重複引用,應當用ifndef/define/endif結構產生預處理塊。
用 #include <filename.h> 格式來引用標準庫的標頭檔案(編譯器將從標準庫目錄開始搜尋)。
用 #include “filename.h” 格式來引用非標準庫的標頭檔案(編譯器將從使用者的工作目錄開始搜尋)。

第2章 程式的版式

一行程式碼只做一件事情,如只定義一個變數,或只寫一條語句。這樣的程式碼容易閱讀,並且方便於寫註釋。
if、for、while、do等語句自佔一行,執行語句不得緊跟其後。不論執行語句有多少都要加{}。這樣可以防止書寫失誤。
儘可能在定義變數的同時初始化該變數(就近原則)。
長表示式要在低優先順序操作符處拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要進行適當的縮排,使排版整齊,語句可讀。

第3章 命名規則

識別符號應當直觀且可以拼讀,可望文知意,不必進行“解碼”。
命名規則儘量與所採用的作業系統或開發工具的風格保持一致。

例如Windows應用程式的識別符號通常採用“大小寫”混排的方式,如AddChild。而Unix應用程式的識別符號通常採用“小寫加下劃線”的方式,如add_child。別把這兩類風格混在一起用。
變數的名字應當使用“名詞”或者“形容詞+名詞”。

第4章 表示式和基本語句

不可將布林變數直接與TRUE、FALSE或者1、0進行比較。
假設布林變數名字為flag,它與零值比較的標準if語句如下:
if (flag)    // 表示flag為真
if (!flag)    // 表示flag為假
應當將整型變數用“==”或“!=”直接與0比較。
假設整型變數的名字為value,它與零值比較的標準if語句如下:
if (value == 0)  
if (value != 0)
不可模仿布林變數的風格而寫成
if (value)    // 會讓人誤解 value是布林變數
if (!value)
不可將浮點變數用“==”或“!=”與任何數字比較。
應當將指標變數用“==”或“!=”與NULL比較。

指標變數的零值是“空”(記為NULL)。儘管NULL的值與0相同,但是兩者意義不同。假設指標變數的名字為p,它與零值比較的標準if語句如下:
        if (p == NULL)    // p與NULL顯式比較,強調p是指標變數
        if (p != NULL)
每個case語句的結尾不要忘了加break,否則將導致多個分支重疊(除非有意使多個分支重疊)。
不要忘記最後那個default分支。即使程式真的不需要default處理,也應該保留語句    default : break; 這樣做並非多此一舉,而是為了防止別人誤以為你忘了default處理。

第5章 常量

在C++ 程式中只使用const常量而不使用巨集常量,即const常量完全取代巨集常量。

第6章 函式設計

const資料成員的初始化只能在類建構函式的初始化表中進行,例如
    class A
    {…
        A(int size);      // 建構函式
        const int SIZE ;
    };
    A::A(int size) : SIZE(size)    // 建構函式的初始化表
    {
      …
    }
    A  a(100); // 物件 a 的SIZE值為100
    A  b(200); // 物件 b 的SIZE值為200
如果引數是指標,且僅作輸入用,則應在型別前加const,以防止該指標在函式體內被意外修改。
return語句不可返回指向“棧記憶體”的“指標”或者“引用”,因為該記憶體在函式體結束時被自動銷燬。例如
    char * Func(void)
    {
        char str[] = “hello world”;    // str的記憶體位於棧上
        …
        return str;     // 將導致錯誤
    }

第7章 記憶體管理

用malloc或new申請記憶體之後,應該立即檢查指標值是否為NULL。防止使用指標值為NULL的記憶體。
不要忘記為陣列和動態記憶體賦初值。防止將未被初始化的記憶體作為右值使用。
避免陣列或指標的下標越界,特別要當心發生“多1”或者“少1”操作。
動態記憶體的申請與釋放必須配對,防止記憶體洩漏
用free或delete釋放了記憶體之後,立即將指標設定為NULL,防止產生“野指標”
注意當陣列作為函式的引數進行傳遞時,該陣列自動退化為同型別的指標。
malloc返回值的型別是void *,所以在呼叫malloc時要顯式地進行型別轉換,將void * 轉換成所需要的指標型別。
用malloc申請一塊長度為length的整數型別的記憶體,程式如下:
        int  *p = (int *) malloc(sizeof(int) * length);

第8章 C++函式的高階特性

成員函式被過載的特徵:
(1)相同的範圍(在同一個類中);
(2)函式名字相同;
(3)引數不同;
(4)virtual關鍵字可有可無。

覆蓋是指派生類函式覆蓋基類函式,特徵是:
(1)不同的範圍(分別位於派生類與基類);
(2)函式名字相同;
(3)引數相同;
(4)基類函式必須有virtual關鍵字。

引數預設值只能出現在函式的宣告中,而不能出現在定義體中。
關鍵字inline必須與函式定義體放在一起才能使函式成為內聯,僅將inline放在函式宣告前面不起任何作用。

第9章 類的建構函式、解構函式與賦值函式

    A(void);                    // 預設的無引數建構函式
    A(const A &a);                // 預設的拷貝建構函式
    ~A(void);                    // 預設的解構函式
    A & operate =(const A &a);    // 預設的賦值函式
如果類存在繼承關係,派生類必須在其初始化表裡呼叫基類的建構函式。
例如
    class A
    {…
        A(int x);        // A的建構函式
}; 
    class B : public A
    {…
        B(int x, int y);// B的建構函式
    };
    B::B(int x, int y)
     : A(x)             // 在初始化表裡呼叫A的建構函式
    {
      …
}  
類的const常量只能在初始化表裡被初始化,因為它不能在函式體內用賦值的方式來初始化。
偷懶的辦法處理拷貝建構函式與賦值函式
       偷懶的辦法是:只需將拷貝建構函式和賦值函式宣告為私有函式,不用編寫程式碼。
例如:
    class A
    { …
      private:
        A(const A &a);                // 私有的拷貝建構函式
        A & operate =(const A &a);    // 私有的賦值函式
    };
在編寫派生類的賦值函式時,注意不要忘記對基類的資料成員重新賦值。

第10章 類的繼承與組合

若在邏輯上B是A的“一種”,並且A的所有功能和屬性對B而言都有意義,則允許B繼承A的功能和屬性。

第11章 其它程式設計經驗

如果輸入引數採用“指標傳遞”,那麼加const修飾可以防止意外地改動該指標,起到保護作用。
當心那些視覺上不易分辨的操作符發生書寫錯誤。
我們經常會把“==”誤寫成“=”,象“||”、“&&”、“<=”、“>=”這類符號也很容易發生“丟1”失誤。然而編譯器卻不一定能自動指出這類錯誤。
變數(指標、陣列)被建立之後應當及時把它們初始化,以防止把未被初始化的變數當成右值使用。
當心變數的初值、預設值錯誤,或者精度不夠。
當心資料型別轉換髮生錯誤。儘量使用顯式的資料型別轉換(讓人們知道發生了什麼事),避免讓編譯器輕悄悄地進行隱式的資料型別轉換。
當心變數發生上溢或下溢,陣列的下標越界。
當心忘記編寫錯誤處理程式,當心錯誤處理程式本身有誤。
當心檔案I/O有錯誤。
避免編寫技巧性很高程式碼。
不要設計面面俱到、非常靈活的資料結構。
如果原有的程式碼質量比較好,儘量複用它。但是不要修補很差勁的程式碼,應當重新編寫。
儘量使用標準庫函式,不要“發明”已經存在的庫函式。
儘量不要使用與具體硬體或軟體環境關係密切的變數。
把編譯器的選擇項設定為最嚴格狀態。

如果可能的話,使用PC-Lint、LogiScope等工具進行程式碼審查。


 轉載請註明出處!http://blog.csdn.net/lsh_2013/article/details/44651603

相關文章