C++高質量程式設計

junnyblog發表於2009-10-23

C++程式設計,對於開發者,都可以寫上二段,但是真正能寫出高質量的程式碼估計還是比較少,同樣我也是學習者,本文作為平時學習日誌吧。隨時都會更新……

[@more@]1、const 資料成員只在某個物件生存期內是常量,而對於整個類而言卻是可變的,
因為類可以建立多個物件,不同的物件其const 資料成員的值可以不同
第6 章 函式設計
1、函式介面的兩個要素是引數和返回值。
避免函式有太多的引數,引數個數儘量控制在5 個以內。
儘量不要使用型別和數目不確定的引數。
2、在函式體的“入口處”,對引數的有效性進行檢查。
很多程式錯誤是由非法引數引起的,我們應該充分理解並正確使用“斷言”(assert)來防止此類錯誤。
斷言assert 是僅在Debug 版本起作用的宏,它用於檢查“不應該”發生的情況。
assert((pvTo != NULL) && (pvFrom != NULL));
3、在函式體的“出口處”,對return 語句的正確性和效率進行檢查。
如果return 語句寫得不好,函式要麼出錯,要麼效率低下。
(1)return 語句不可返回指向“棧記憶體”的“指標”或者“引用”,因為該記憶體在函式體結束時被自動銷燬。
4、引用與指標的比較
引用的一些規則如下:
(1)引用被建立的同時必須被初始化(指標則可以在任何時候被初始化)。
(2)不能有NULL 引用,引用必須與合法的儲存單元關聯(指標則可以是NULL)。
(3)一旦引用被初始化,就不能改變引用的關係(指標則可以隨時改變所指的物件)
引用的主要功能是傳遞函式的引數和返回值。
C++語言中,函式的引數和返回值的傳遞方式有三種:值傳遞、指標傳遞和引用傳遞。
5、指標能夠毫無約束地操作記憶體中的如何東西,儘管指標功能強大,但是非常危險。
第7 章 記憶體管理
1、記憶體分配方式有三種:
(1) 從靜態儲存區域分配。
記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個執行期間都存在。例如全域性變數,static 變數。
(2) 在棧上建立。在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單元自動被釋放。
棧記憶體分配運算內建於處理器的指令集中,效率很高,但是分配的記憶體容量有限。
(3) 從堆上分配,亦稱動態記憶體分配。程式在執行的時候用malloc 或new 申請任意多少的記憶體,
程式設計師自己負責在何時用free 或delete 釋放記憶體。動態記憶體的生存期由我們決定,使用非常靈活,但問題也最多。
2、常見的記憶體錯誤及其對策
(1)記憶體分配未成功,卻使用了它。
常用解決辦法是,在使用記憶體之前檢查指標是否為NULL。
如果指標p 是函式的引數,那麼在函式的入口處用assert(p!=NULL)進行檢查。
如果是用malloc 或new 來申請記憶體,應該用if(p==NULL)或if(p!=NULL)進行防錯處理。
(2)記憶體分配雖然成功,但是尚未初始化就引用它。
所以無論用何種方式建立陣列,都別忘了賦初值,即便是賦零值也不可省略,不要嫌麻煩。
(3)記憶體分配成功並且已經初始化,但操作越過了記憶體的邊界。
(4)忘記了釋放記憶體,造成記憶體洩露。
含有這種錯誤的函式每被呼叫一次就丟失一塊記憶體。動態記憶體的申請與釋放必須配對,
程式中malloc 與free 的使用次數一定要相同,否則肯定有錯誤(new/delete 同理)。
(5)釋放了記憶體卻繼續使用它。
(1)程式中的物件呼叫關係過於複雜,實在難以搞清楚某個物件究竟是否已經釋放了內
存,此時應該重新設計資料結構,從根本上解決物件管理的混亂局面。
(2)函式的return 語句寫錯了,注意不要返回指向“棧記憶體”的“指標”或者“引用”,
因為該記憶體在函式體結束時被自動銷燬。
(3)使用free 或delete 釋放了記憶體後,沒有將指標設定為NULL。導致產生“野指標”。
3、用malloc 或new 申請記憶體之後,應該立即檢查指標值是否為NULL。防止使用指標值為NULL 的記憶體。
不要忘記為陣列和動態記憶體賦初值。防止將未被初始化的記憶體作為右值使用。
避免陣列或指標的下標越界,特別要當心發生“多1”或者“少1”操作。
動態記憶體的申請與釋放必須配對,防止記憶體洩漏。
用free 或delete 釋放了記憶體之後,立即將指標設定為NULL,防止產生“野指標”。
4、指標與陣列的對比
C++/C 程式中,指標和陣列在不少地方可以相互替換著用。
陣列要麼在靜態儲存區被建立(如全域性陣列),要麼在棧上被建立。陣列名對應著(而不是指向)一塊記憶體。
指標可以隨時指向任意型別的記憶體塊,它的特徵是“可變”,所以我們常用指標來操作動態記憶體。
char *p = “world”;指標p 指向常量字串“world”(位於靜態儲存區,內容為world),常量字串的內容是不可以被修改的。
從語法上看,編譯器並不覺得語句p[0]= ‘X’有什麼不妥,但是該語句企圖修改常量字串的內容而導致執行錯誤。
5、C++/C 語言沒有辦法知道指標所指的記憶體容量,除非在申請記憶體時記住它。
注意當陣列作為函式的引數進行傳遞時,該陣列自動退化為同型別的指標。
6、指標引數是如何傳遞記憶體的?
如果函式的引數是一個指標,不要指望用該指標去申請動態記憶體。
7、編譯器總是要為函式的每個引數製作臨時副本,指標引數p 的副本是 _p,編譯器使 _p = p。
如果函式體內的程式修改了_p 的內容,就導致引數p 的內容作相應的修改。這就是指標可以用作輸出引數的原因。
在本例中,_p 申請了新的記憶體,只是把_p 所指的記憶體地址改變了,但是p 絲毫未變。所以函式GetMemory
並不能輸出任何東西。事實上,每執行一次GetMemory 就會洩露一塊記憶體,因為沒有用free 釋放記憶體。
8、如果非得要用指標引數去申請記憶體,那麼應該改用“指向指標的指標“。
由於“指向指標的指標”這個概念不容易理解,我們可以用函式返回值來傳遞動態記憶體。
這裡強調不要用return 語句返回指向“棧記憶體”的指標,因為該記憶體在函式結束時自動消亡。
9、發現指標p 被free 以後其地址仍然不變(非NULL),只是該地址對應的記憶體是垃圾,p 成了“野指標”。
如果此時不把p 設定為NULL,會讓人誤以為p 是個合法的指標。
10、動態記憶體會被自動釋放嗎?
(1)指標消亡了,並不表示它所指的記憶體會被自動釋放。
(2)記憶體被釋放了,並不表示指標會消亡或者成了NULL 指標。
11、“野指標”不是NULL 指標,是指向“垃圾”記憶體的指標。
“野指標”的成因主要有兩種:
(1)指標變數沒有被初始化。任何指標變數剛被建立時不會自動成為NULL 指標,它的預設值是隨機的,
它會亂指一氣。所以,指標變數在建立的同時應當被初始化,要麼將指標設定為NULL,要麼讓它指向合法的記憶體。
(2)指標p 被free 或者delete 之後,沒有置為NULL,讓人誤以為p 是個合法的指標。
(3)指標操作超越了變數的作用範圍。
12、有了malloc/free 為什麼還要new/delete ?
1、 malloc 與free 是C++/C 語言的標準庫函式,new/delete 是C++的運算子。
2、對於非內部資料型別的物件而言,光用maloc/free 無法滿足動態物件的要求。物件
在建立的同時要自動執行建構函式, 物件在消亡之前要自動執行解構函式。
13、記憶體耗盡怎麼辦?
如果在申請動態記憶體時找不到足夠大的記憶體塊,malloc 和new 將返回NULL 指標,
宣告記憶體申請失敗。通常有三種方式處理“記憶體耗盡”問題。
(1)判斷指標是否為NULL,如果是則馬上用return 語句終止本函式。
(2)判斷指標是否為NULL,如果是則馬上用exit(1)終止整個程式的執行。
(3)為new 和malloc 設定異常處理函式。
14、int *p = (int *) malloc(sizeof(int) * length);
如果p 是NULL 指標,那麼free 對p 無論操作多少次都不會出問題。如果p 不是NULL 指標,那麼free 對p
連續操作兩次就會導致程式執行錯誤。
int *p2 = new int[length];因為new 內建了sizeof、型別轉換和型別安全檢查功能。
15、我的經驗教訓是:
(1)越是怕指標,就越要使用指標。不會正確使用指標,肯定算不上是合格的程式設計師。
(2)必須養成“使用偵錯程式逐步跟蹤程式”的習慣,只有這樣才能發現問題的本質。
第8 章 C++函式的高階特性
1、對比於C 語言的函式,C++增加了過載(overloaded)、內聯(inline)、const 和virtual四種新機制。
其中過載和內聯機制既可用於全域性函式也可用於類的成員函式,const 與virtual 機制僅用於類的成員函式。
2、成員函式被過載的特徵:
(1)相同的範圍(在同一個類中);
(2)函式名字相同;
(3)引數不同;
(4)virtual 關鍵字可有可無。
覆蓋是指派生類函式覆蓋基類函式,特徵是:
(1)不同的範圍(分別位於派生類與基類);
(2)函式名字相同;
(3)引數相同;
(4)基類函式必須有virtual 關鍵字。
3、運算子過載
在C++語言中,可以用關鍵字operator 加上運算子來表示函式,叫做運算子過載。
Complex Add(const Complex &a, const Complex &b);
Complex operator +(const Complex &a, const Complex &b);
運算子與普通函式在呼叫時的不同之處是:對於普通函式,引數出現在圓括號內;而對於運算子,引數出現在其左、右側。
4、函式內聯
C++ 語言支援函式內聯,其目的是為了提高函式的執行效率(速度)。
定義在類宣告之中的成員函式將自動地成為行內函數.
內聯是以程式碼膨脹(複製)為代價,僅僅省去了函式呼叫的開銷,從而提高函式的執行效率。
每一處行內函數的呼叫都要複製程式碼,將使程式的總程式碼量增大,消耗更多的記憶體空間。
第9 章 類的建構函式、解構函式與賦值函式
1、根據經驗,不少難以察覺的程式錯誤是由於變數沒有被正確初始化或清除造成的,而初始化和清除工作很容易被人遺忘。
第11 章 其它程式設計經驗
1、const 更大的魅力是它可以修飾函式的引數、返回值,甚至函式的定義體。
2、對於非內部資料型別的輸入引數,應該將“值傳遞”的方式改為“const 引用傳遞”,目的是提高效率。
例如將void Func(A a) 改為void Func(const A &a)。
對於內部資料型別的輸入引數,不要將“值傳遞”的方式改為“const 引用傳遞”。否則既達不到提高效率的目的,
又降低了函式的可理解性。例如void Func(int x) 不應該改為void Func(const int &x)。
3、任何不會修改資料成員的函式都應該宣告為const 型別。
4、變數(指標、陣列)被建立之後應當及時把它們初始化,以防止把未被初始化的變數當成右值使用。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22630256/viewspace-1028090/,如需轉載,請註明出處,否則將追究法律責任。

相關文章