[推薦]《C/C++高質量編程》筆記

引言:《C/C++高質量編程》這本書,很好的給出了一些編程規範,是每一個C/C++程序員都應該認真讀取的一本書。幾次閱讀,幾次忘記,今天把一些關鍵點記錄下來,便於以後繼續學習和參考。建議此書和谷歌C++編碼規範一起使用,相得益彰。


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

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

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

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

【規則2-5-1代碼行最大長度宜控制在7080個字符以內。代碼行不要過長,否則眼睛看不過來,也不便於打印。

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

例如:

char  *name;

    int   *x, y;  // 此處y不會被誤解爲指針

【規則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;

【規則3-2-4靜態變量加前綴s_(表示static)。

【規則3-2-5如果不得已需要全局變量,則使全局變量加前綴g_(表示global)。

const數據成員只在某個對象生存期內是常量,而對於整個類而言卻是可變的,因爲類可以創建多個對象,不同的對象其const數據成員的值可以不同。

    不能在類聲明中初始化const數據成員。

const數據成員的初始化只能在類構造函數的初始化表中進行,例如

    class A

    {…

        A(int size);      // 構造函數

        const int SIZE ;

    };

    A::A(int size) : SIZE(size)    // 構造函數的初始化表

 【建議6-2-2如果函數的返回值是一個對象,有些場合用“引用傳遞”替換“值傳遞”可以提高效率。

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

     如果函數有返回值,那麼函數的“出口處”是return語句。我們不要輕視return語句。如果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在函數結束時被銷燬(調用析構函數)。然而“創建一個臨時對象並返回它”的過程是不同的,編譯器直接把臨時對象創建並初始化在外部存儲單元中,省去了拷貝和析構的化費,提高了效率。

類似地,我們不要將 

return int(x + y); // 創建一個臨時變量並返回它

寫成

int temp = x + y;

return temp;

由於內部數據類型如int,float,double的變量不存在構造函數與析構函數,雖然該“臨時變量的語法”不會提高多少效率,但是程序更加簡潔易讀。


斷言assert是僅在Debug版本起作用的宏,它用於檢查“不應該”發生的情況。如果程序在assert處終止了,並不是說含有該assert的函數有錯誤,而是調用者出了差錯,assert可以幫助我們找到發生錯誤的原因。

內存分配方式

內存分配方式有三種:

(1)從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量,static變量。

(2)在棧上創建。在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。

(3)從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc或new申請任意多少的內存,程序員自己負責在何時用free或delete釋放內存。動態內存的生存期由我們決定,使用非常靈活,但問題也最多。


注意不要返回指向“棧內存”的“指針”或者“引用”,因爲該內存在函數體結束時被自動銷燬。

 【規則7-2-5freedelete釋放了內存之後,立即將指針設置爲NULL,防止產生“野指針”。

內存耗盡怎麼辦?

  如果在申請動態內存時找不到足夠大的內存塊,malloc和new將返回NULL指針,宣告內存申請失敗。通常有三種方式處理“內存耗盡”問題。

(1)判斷指針是否爲NULL,如果是則馬上用return語句終止本函數。

2)判斷指針是否爲NULL,如果是則馬上用exit(1)終止整個程序的運行。

3)爲newmalloc設置異常處理函數。

越是怕指針,就越要使用指針。不會正確使用指針,肯定算不上是合格的程序員。

關鍵字inline必須與函數定義體放在一起才能使函數成爲內聯,僅將inline放在函數聲明前面不起任何作用。

使用const提高函數的健壯性

const常量,const更大的魅力是它可以修飾函數的參數、返回值,甚至函數的定義體。

用const修飾函數的參數,const只能修飾輸入參數。

如果輸入參數採用“指針傳遞”,那麼加const修飾可以防止意外地改動該指針,起到保護作用。

如果輸入參數採用“值傳遞”,由於函數將自動產生臨時變量用於複製該參數,該輸入參數本來就無需保護,所以不要加const修飾。

例如不要將函數void Func1(int x) 寫成void Func1(const int x)。同理不要將函數void Func2(A a) 寫成void Func2(const A a)。其中A爲用戶自定義的數據類型。

對於非內部數據類型的輸入參數,應該將“值傳遞”的方式改爲“const引用傳遞”,目的是提高效率。例如將void Func(A a) 改爲void Func(const A &a)。

對於內部數據類型的輸入參數,不要將“值傳遞”的方式改爲“const引用傳遞”。否則既達不到提高效率的目的,又降低了函數的可理解性。例如void Func(int x) 不應該改爲void Func(const int &x)。

如果給以“指針傳遞”方式的函數返回值加const修飾,那麼函數返回值(即指針)的內容不能被修改,該返回值只能被賦給加const修飾的同類型指針。

例如函數

        const char * GetString(void);

如下語句將出現編譯錯誤:

        char *str = GetString();

正確的用法是

        const char *str = GetString();

如果函數返回值採用“值傳遞方式”,由於函數會把返回值複製到外部臨時的存儲單元中,加const修飾沒有任何價值。

    例如不要把函數int GetInt(void) 寫成const int GetInt(void)。

    同理不要把函數A GetA(void) 寫成const A GetA(void),其中A爲用戶自定義的數據類型。

    如果返回值不是內部數據類型,將函數A GetA(void) 改寫爲const A & GetA(void)的確能提高效率。但此時千萬千萬要小心,一定要搞清楚函數究竟是想返回一個對象的“拷貝”還是僅返回“別名”就可以了,否則程序會出錯。

函數返回值採用“引用傳遞”的場合並不多,這種方式一般只出現在類的賦值函數中,目的是爲了實現鏈式表達。





發佈了90 篇原創文章 · 獲贊 110 · 訪問量 69萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章