C++高質量編程

C++高質量編程

C++編程,對於開發者,均可以寫上二段,可是真正能寫出高質量的代碼估計仍是比較少,一樣我也是學習者,本文做爲平時學習日誌吧。隨時都會更新……程序員

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