一、 在多重循環中,若是有可能,應當將最長的循環放在最內層,最短的循環放在最外層,以減小 CPU 跨切循環層的次數。ios
二、 若是循環體內存在邏輯判斷,而且循環次數很大,宜將邏輯判斷移到循環體的外面。c++
三、 即便程序真的不須要default 處理,也應該保留語句 default : break。程序員
四、 C 語言用#define 來定義常量(稱爲宏常量)。 C++ 語言除了 #define 外還能夠用 const 來定義常量(稱爲 const 常量)。數組
五、 const 與 #define 的比較安全
Ø const 常量有數據類型,而宏常量沒有數據類型。編譯器能夠對前者進行類型安全檢查。而對後者只進行字符替換,沒有類型安全檢查,而且在字符替換可能會產生意料不到的錯誤(邊際效應)。函數
Ø 有些集成化的調試工具能夠對const 常量進行調試,可是不能對宏常量進行調試。工具
六、 須要對外公開的常量放在頭文件中,不須要對外公開的常量放在定義文件的頭部。爲便於管理,能夠把不一樣模塊的常量集中存放在一個公共的頭文件中。this
七、 const 數據成員只在某個對象生存期內是常量,而對於整個類而言倒是可變的,由於類能夠建立多個對象,不一樣的對象其 const 數據成員的值能夠不一樣。spa
八、 不能在類聲明中初始化const 數據成員。如下用法是錯誤的,由於類的對象未被建立時,編譯器不知道 SIZE 的值是什麼。指針
class A
{…
const intSIZE = 100; // 錯誤,企圖在類聲明中初始化 const 數據成員
intarray[SIZE]; // 錯誤,未知的 SIZE
};
九、 const 數據成員的初始化只能在類構造函數的初始化表中進行。
十、 枚舉常量不會佔用對象的存儲空間,它們在編譯時被所有求值。枚舉常量的缺點是:它的隱含數據類型是整數,其最大值有限,且不能表示浮點數(如 PI=3.14159)。
十一、 通常地,應將目的參數放在前面,源參數放在後面。
十二、 有時候函數本來不須要返回值,但爲了增長靈活性如支持鏈式表達,能夠附加返回值。
1三、 不少程序錯誤是由非法參數引發的,咱們應該充分理解並正確使用「斷言」( assert)來防止此類錯誤。
1四、 return 語句不可返回指向「棧內存」的「指針」或者「引用」,由於該內存在函數體結束時被自動銷燬。
1五、 斷言 assert 是僅在 Debug 版本起做用的宏,它用於檢查「不該該」發生的狀況。
1六、 引用的一些規則以下:
Ø 引用被建立的同時必須被初始化(指針則能夠在任什麼時候候被初始化)。
Ø 不能有 NULL 引用,引用必須與合法的存儲單元關聯(指針則能夠是 NULL)。
Ø 一旦引用被初始化,就不能改變引用的關係(指針則能夠隨時改變所指的對象)。
1七、 內存分配方式有三種:
Ø 從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。例如全局變量, static 變量。
Ø 在棧上建立。在執行函數時,函數內局部變量的存儲單元均可以在棧上建立,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,可是分配的內存容量有限。
Ø 從堆上分配,亦稱動態內存分配。程序在運行的時候用 malloc 或 new 申請任意多少的內存,程序員本身負責在什麼時候用 free 或 delete 釋放內存。動態內存的生存期由咱們決定,使用很是靈活,但問題也最多。
1八、 常見的內存錯誤及其對策以下:
Ø 內存分配未成功,卻使用了它。
Ø 內存分配雖然成功,可是還沒有初始化就引用它。
Ø 內存分配成功而且已經初始化,但操做越過了內存的邊界。
Ø 忘記了釋放內存,形成內存泄露。
Ø 釋放了內存卻繼續使用它。(使用 free 或 delete 釋放了內存後,沒有將指針設置爲 NULL。致使產生「野指針」。)
1九、 數組名對應着(而不是指向)一塊內存,其地址與容量在生命期內保持不變,只有數組的內容能夠改變。
20、 不能對數組名進行直接複製與比較。
2一、 用運算符 sizeof 能夠計算出數組的容量(字節數)。sizeof(p)獲得的是一個指針變量的字節數,至關於 sizeof(char*),而不是 p 所指的內存容量。 C++/C 語言沒有辦法知道指針所指的內存容量,除非在申請內存時記住它。
2二、 注意當數組做爲函數的參數進行傳遞時,該數組自動退化爲同類型的指針。
2三、 若是函數的參數是一個指針,不要期望用該指針去申請動態內存。(能夠用指針的指針)
2四、 發現指針 p 被 free 之後其地址仍然不變(非 NULL),只是該地址對應的內存是垃圾, p 成了「野指針」。若是此時不把 p 設置爲 NULL,會讓人誤覺得 p 是個合法的指針。
2五、 free()以後因爲指針所指向的內存已經被釋放,因此其它代碼有機會改寫其中的內容,至關於該指針今後指向了本身沒法控制的地方,也稱爲野指針。
2六、 咱們發現指針有一些「似是而非」的特徵:
Ø 指針消亡了,並不表示它所指的內存會被自動釋放。
Ø 內存被釋放了,並不表示指針會消亡或者成了 NULL 指針。
2七、 「野指針」的成因主要有兩種:
Ø 指針變量沒有被初始化。任何指針變量剛被建立時不會自動成爲 NULL 指針,它的缺省值是隨機的,它會亂指一氣。因此,指針變量在建立的同時應當被初始化,要麼將指針設置爲 NULL,要麼讓它指向合法的內存。
Ø 指針 p 被 free 或者 delete 以後,沒有置爲 NULL,讓人誤覺得 p 是個合法的指針。
Ø 指針操做超越了變量的做用範圍。
2八、 malloc 與 free 是 C++/C 語言的標準庫函數,new/delete 是 C++的運算符。它們均可用於申請動態內存和釋放內存。
2九、 對於非內部數據類型的對象而言,光用 maloc/free 沒法知足動態對象的要求。對象在建立的同時要自動執行構造函數,對象在消亡以前要自動執行析構函數。因爲malloc/free 是庫函數而不是運算符,不在編譯器控制權限以內,不可以把執行構造函數和析構函數的任務強加於 malloc/free。所以 C++語言須要一個能完成動態內存分配和初始化工做的運算符 new,以及一個能完成清理與釋放內存工做的運算符 delete。注意 new/delete 不是庫函數。
30、 因爲內部數據類型的「對象」沒有構造與析構的過程,對它們而言 malloc/free 和new/delete 是等價的。
3一、 若是用 free 釋放「 new 建立的動態對象」,那麼該對象因沒法執行析構函數而可能致使程序出錯。若是用 delete 釋放「 malloc 申請的動態內存」,理論上講程序不會出錯,可是該程序的可讀性不好。因此 new/delete 必須配對使用, malloc/free 也同樣。
3二、 malloc/free 的使用咱們應當把注意力集中在兩個要素上:「類型轉換」和「 sizeof」。
Ø malloc 返回值的類型是 void *,因此在調用 malloc 時要顯式地進行類型轉換,將 void * 轉換成所須要的指針類型。
Ø malloc 函數自己並不識別要申請的內存是什麼類型,它只關心內存的總字節數。
3三、 若是 p 是 NULL 指針,那麼 free 對 p 不管操做多少次都不會出問題。若是 p 不是 NULL 指針,那麼 free 對 p連續操做兩次就會致使程序運行錯誤。
3四、 若是用 new 建立對象數組,那麼只能使用對象的無參數構造函數。例如
Obj *objects = newObj[100]; // 建立100 個動態對象不能寫成
Obj *objects = newObj[100](1);// 建立100 個動態對象的同時賦初值 1
在用delete 釋放對象數組時,留意不要丟了符號‘ []’。例如
delete []objects; // 正確的用法
delete objects; // 錯誤的用法
後者至關於delete objects[0],漏掉了另外 99 個對象。
3五、 c++中爲何static成員函數不能聲明爲const?(注意是成員函數)
答:這是C++的規則,const修飾符用於表示函數不能修改爲員變量的值,該函數必須是含有this指針的類成員函數,函數調用方式爲thiscall,而類中的static函數本質上是全局函數,調用規約是__cdecl或__stdcall,不能用const來修飾它。
3六、 重載和內聯機制既可用於全局函數也可用於類的成員函數, const 與virtual 機制僅用於類的成員函數。
3七、 只能靠參數而不能靠返回值類型的不一樣來區分重載函數。
3八、 注意並非兩個函數的名字相同就能構成重載。全局函數和類的成員函數同名不算重載,由於函數的做用域不一樣。
3九、 小心隱式類型轉換致使重載函數產生二義性。
40、 成員函數被重載的特徵:
Ø 相同的範圍(在同一個類中);
Ø 函數名字相同;
Ø 參數不一樣;
Ø virtual 關鍵字無關緊要。
4一、 覆蓋是指派生類函數覆蓋基類函數,特徵是:
Ø 不一樣的範圍(分別位於派生類與基類);
Ø 函數名字相同;
Ø 參數相同;
Ø 基類函數必須有virtual 關鍵字。
4二、 「隱藏」是指派生類的函數屏蔽了與其同名的基類函數,規則以下:
Ø 若是派生類的函數與基類的函數同名,可是參數不一樣。此時,不論有無 virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)。【參數不一樣必定會被隱藏】
Ø 若是派生類的函數與基類的函數同名,而且參數也相同,可是基類函數沒有 virtual關鍵字。此時,基類的函數被隱藏(注意別與覆蓋混淆)。【沒有virtual必定會被隱藏】
4三、 參數缺省值只能出如今函數的聲明中,而不能出如今定義體中。
4四、 若是函數有多個參數,參數只能從後向前挨個兒缺省,不然將致使函數調用語句怪模怪樣。
4五、 在 C++運算符集合中,有一些運算符是不容許被重載的。這種限制是出於安全方面的考慮,可防止錯誤和混亂。
Ø 不能改變 C++內部數據類型(如int,float 等)的運算符。
Ø 不能重載‘ .’,由於‘ .’在類中對任何成員都有意義,已經成爲標準用法。
Ø 不能重載目前 C++運算符集合中沒有的符號,如#,@,$等。緣由有兩點,一是難以理解,二是難以肯定優先級。
Ø 對已經存在的運算符進行重載時,不能改變優先級規則,不然將引發混亂。
4六、 在 C 程序中,能夠用宏代碼提升執行效率。宏代碼自己不是函數,但使用起來象函數。預處理器用複製宏代碼的方式代替函數調用,省去了參數壓棧、生成彙編語言的 CALL調用、返回參數、執行 return 等過程,從而提升了速度。使用宏代碼最大的缺點是容易出錯,預處理器在複製宏代碼時經常產生意想不到的邊際效應。使用宏代碼還有另外一種缺點:沒法操做類的私有數據成員。
4七、 C++ 語言的函數內聯機制既具有宏代碼的效率,又增長了安全性,並且能夠自由操做類的數據成員。因此在 C++ 程序中,應該用內聯函數取代全部宏代碼。
4八、 關鍵字 inline 必須與函數定義體放在一塊兒才能使函數成爲內聯,僅將 inline 放在函數聲明前面不起任何做用。
4九、 定義在類聲明之中的成員函數將自動地成爲內聯函數。
50、 內聯是以代碼膨脹(複製)爲代價,僅僅省去了函數調用的開銷,從而提升函數的執行效率。
5一、 如下狀況不宜使用內聯:
Ø 若是函數體內的代碼比較長,使用內聯將致使內存消耗代價較高。
Ø 若是函數體內出現循環,那麼執行函數體內代碼的時間要比函數調用的開銷大。
5二、 每一個類只有一個析構函數和一個賦值函數,但能夠有多個構造函數(包含一個拷貝構造函數,其它的稱爲普通構造函數)。對於任意一個類 A,若是不想編寫上述函數,C++編譯器將自動爲 A 產生四個缺省的函數,如:
Ø A(void); //缺省的無參數構造函數
Ø A(const A &a); //缺省的拷貝構造函數
Ø ~A(void); //缺省的析構函數
Ø A & operate =(const A &a); //缺省的賦值函數
5三、 構造函數與析構函數的另外一個特別之處是沒有返回值類型,這與返回值類型爲 void 的函數不一樣。
5四、 構造函數有個特殊的初始化方式叫「初始化表達式表」(簡稱初始化表)。初始化表位於函數參數表以後,卻在函數體 {} 以前。這說明該表裏的初始化工做發生在函數體內的任何代碼被執行以前。
5五、 類的 const 常量只能在初始化表裏被初始化,由於它不能在函數體內用賦值的方式來初始化。
5六、 類的數據成員的初始化能夠採用初始化表或函數體內賦值兩種方式,這兩種方的效率不徹底相同。
5七、 非內部數據類型的成員對象應當採用第一種方式初始化,以獲取更高的效率。
5八、 構造和析構的次序:構造從類層次的最根處開始,在每一層中,首先調用基類的構造函數,而後調用成員對象的構造函數。析構則嚴格按照與構造相反的次序執行,該次序是惟一的,不然編譯器將沒法自動執行析構過程。
5九、 成員對象初始化的次序徹底不受它們在初始化表中次序的影響,只由成員對象在類中聲明的次序決定。這是由於類的聲明是惟一的,而類的構造函數能夠有多個,所以會有多個不一樣次序的初始化表。若是成員對象按照初始化表的次序進行構造,這將致使析構函數沒法獲得惟一的逆序。
60、 拷貝構造函數是在對象被建立時調用的,而賦值函數只能被已經存在了的對象調用。
6一、 深度複製(針對《c++ primer plus》一書string例題的解釋):複製構造函數應當複製字符串並將副本的地址賦給str成員,而不只僅是複製字符串的地址,這樣每一個對象都有本身的字符串,而不是引用另外一個對象的字符串,調用析構函數時都將釋放不一樣的字符串,而不會試圖去釋放已經被釋放的字符串。
6二、 什麼時候調用複製構造函數:新建一個對象並將其初始化爲同類現有對象時,複製構造函數都將被調用。每當程序生成了對象副本時,編譯器都將使用複製構造函數。具體的說,當函數按值傳遞對象或函數返回對象時,都將使用複製構造函數。記住,按值傳遞意味着建立原始變量的一個副本。編譯器生成臨時對象時,也將使用複製構造函數。什麼時候生成臨時對象隨編譯器而異,但不管那種編譯器,當按值傳遞和返回對象時,都將調用複製構造函數。
6三、 String類的賦值函數要作的工做:
Ø 自我檢查賦值的狀況
Ø 釋放成員指針之前指向的內存
Ø 複製數據而不只僅是數據的地址
Ø 指向一個返回對象的引用(不要將 return *this 錯寫成 return this)
6四、 基類與派生類的析構函數應該爲虛(即加 virtual關鍵字)。
#include <iostream.h>
classBase
{
public:
virtual ~Base() { cout<< "~Base" << endl ; }
};
classDerived : public Base
{
public:
virtual ~Derived() { cout<< "~Derived" << endl; }
};
voidmain(void)
{
Base * pB = new Derived; // upcast
delete pB;
}
輸出結果爲:
~Derived
~Base
若是析構函數不爲虛,那麼輸出結果爲
~Base
6五、 若是使用指向對象的引用或指針來調用虛方法,程序將使用爲對象類型定義的方法,而不使用爲引用或指針類型定義的方法。這稱爲動態聯編或晚期聯編。這種行爲很是重要,由於這樣基類指針或引用能夠指向派生類對象。
6六、 用 const 修飾函數的參數: 若是參數做輸出用,不論它是什麼數據類型,也不論它採用「指針傳遞」仍是「引用傳遞」,都不能加 const 修飾,不然該參數將失去輸出功能。const 只能修飾輸入參數。(參數作輸出用不能加const,const只能修飾輸入參數)
6七、 若是輸入參數採用「值傳遞」,因爲函數將自動產生臨時變量用於複製該參數,該輸入參數原本就無需保護,因此不要加 const 修飾。
6八、 const 成員函數的聲明看起來怪怪的: const 關鍵字只能放在函數聲明的尾部,大概是由於其它地方都已經被佔用了。