系統從新學習C++語言部分,記錄重要但易被忽略的,關鍵但易被遺忘的。編程
一、不能重載的運算符函數
1 sizeof sizeof運算符 2 . 成員運算符 3 .* 成員指針運算符 4 :: 做用域解析運算符 5 ?: 條件運算符 6 typeid 一個RTTI運算符 7 const_cast 強制類型轉換運算符 8 dynamic_cast 強制類型轉換運算符 9 reinterpret_cast 強制類型轉換運算符 10 static_cast 強制類型轉換運算符
二、只能經過成員函數重載的運算符學習
1 = 賦值運算符 2 () 函數調用運算符 3 [] 下標運算符 4 -> 經過指針訪問類成員的運算符
三、關於類的類型轉換函數,C++11支持對其使用explicit關鍵字,使其沒法進行隱式類型轉換。spa
四、對於定義了一個以上的轉換函數的類,編譯器在某些狀況下(如將一個對象直接賦值給一個基本類型,或用cout輸出時)沒法肯定應該使用哪個轉換函數(進行隱式類型轉換),所以將出現二義性錯誤,但只有一個轉換函數時,編譯器只能選擇這一個,所以不會出錯。指針
五、將新對象顯示地初始化爲現有對象時將調用拷貝構造函數,默認的拷貝構造函數將除靜態成員之外的全部成員按值賦值。code
1 String a(b); 2 String a = b; 3 String a = String(b); 4 String * a = new String(b);
將已有的對象賦值給另外一個已有的對象時,會調用賦值構造函數。對象
六、靜態成員函數不與特定的對象關聯,所以只能使用靜態數據成員(單例模式)。blog
七、對於使用定位new運算符建立的對象,應顯式地調用其析構函數,須要注意的是,在析構時,對象的析構順序應該與建立順序相反,由於晚建立的對象可能依賴於早建立的對象,另外,只有當全部對象被銷燬後,才能釋放存儲這些對象地緩衝區。繼承
八、只有構造函數可使用初始化列表語法,對於const類成員(C++11以前)和聲明爲引用的類成員,必須使用這種語法,由於它們只能在被建立時初始化。內存
九、 公有繼承是最經常使用的繼承方式,它創建一種is-a關係,即派生類對象也是一個基類對象,能夠對基類對象執行的任何操做,也能夠對派生類對象執行。公有繼承不創建has-a關係;公有繼承不創建is-like-a關係;公有繼承不創建is-implemented-as-a(做爲……來實現)關係;公有繼承不創建uses-a關係。在C++中,徹底可使用公有繼承來實現has-a、is-implemented-as-a或use-a關係,然而這樣作一般會致使編程方面的問題,所以,仍是堅持使用is-a關係吧。
十、 在基類的方法中使用關鍵字virtual可以使該方法在基類已經全部派生類(包括從派生類派生出來的類)中是虛的,也就是說只要函數名相同,只須要在基類中聲明爲虛函數,那它的派生類中,包括派生派生類中的這個函數都是虛函數,但爲了可讀性,通常派生類中的虛函數也用virtual聲明。
十一、若是從新定義繼承的方法,應確保與原來的原型徹底相同,但若是返回類型是基類引用或指針,則能夠修改成指向派生類的引用或指針,這種特性被稱爲返回類型協變,由於容許返回類型隨類類型的變化而變化。
十二、若是基類聲明被重載了,則應在派生類中從新定義全部的基類版本,若是隻在派生類中只定義了一個版本,則另外的版本將被隱藏。
1三、C++容許純虛函數有定義,但不能在類內定義,能夠在實現文件中定義。
1四、當基類和派生類都爲至少一個成員採用了動態內存分配時,派生類的析構函數,拷貝構造函數,賦值構造函數都必須使用相應的基類方法來處理基類元素。對於析構函數,這是自動完成的;對於拷貝構造函數,是經過初始化列表中調用積累的拷貝構造函數完成的;對於賦值構造函數是經過使用做用域解析運算符顯示地調用基類的賦值構造函數完成的。
1五、當派生類的友元函數須要訪問基類中的非公有成員時,作法是在派生類的友元函數中將派生類的引用強制類型轉換爲基類的引用。
1六、當類的初始化列表包含多個項目時,這些項目的初始化順序爲聲明它們的順序,而不是他們在初始化列表中的順序,若是代碼使用一個成員的值做爲另外一個成員初始化表達式的一部分時,初始化順序就須要引發注意。
1七、在繼承時,private是默認值,所以忽略訪問限定符也將致使私有繼承。
1八、在私有繼承時,訪問基類方法,須要使用類名加做用域解析運算符訪問;訪問基類對象(例如將基類對象看成返回值時),能夠將派生類強制類型轉換爲基類;訪問基類友元函數時,由於友元函數不屬於成員函數,所以不能顯式地限定函數名去訪問,能夠經過顯式地轉換爲基類來調用正確的函數。
1九、一般,應該使用包含來創建has-a關係,若是新類須要訪問原有類的保護成員,或須要從新定義虛函數,則應使用私有繼承。
20、對於指向對象的類或引用中的隱式向上轉換,公有繼承直接支持,保護繼承只在派生類中支持,私有繼承不支持。
2一、若是要使私有繼承的基類中的私有函數能夠在派生類外訪問,能夠聲明一個公有函數,再去調用基類的私有函數,另外還可使用using聲明:
1 class A:private B 2 { 3 public: 4 using B:foo; // 只須要有函數簽名便可,全部重載版本均可以使用 5 }
另外一種老式的方法是將基類方法名放在派生類的共有部分。
2二、在多重繼承中,若是出現了菱形繼承,頂端的基類應該使用虛繼承,防止底端的派生類包含兩份基類,同時,在構造函數的初始化列表裏應該顯式地調用頂端基類的構造函數,對於虛基類必須這樣作,不然將使用虛基類的默認構造函數,而且此時的虛基類沒法經過中間的派生類去完成構造,但對於非虛基類,這是非法的。
2三、在混合使用虛基類和非虛基類,類經過多條虛途徑和非虛途徑繼承某個特定的基類時,該類將包含,一個表示全部的虛途徑即基類子對象和分別表示各條非虛途徑的多個基類子對象。
2四、使用非虛基類時,二義性的規則很簡單,只要類從不一樣類那裏繼承了同名的成員,使用時沒有用類名限定,則必定會致使二義性,但虛基類時不必定會致使二義性,若是某個名稱優先於其餘名稱,則使用它。優先的規則是,派生類中的名稱優先於直接或間接祖先類中的相同名稱。
2五、有間接虛基類的派生類包含直接調用間接基類構造函數的構造函數,這對於間接非虛基類是非法的。
2六、 在類模板中,模板代碼不能修改參數的值,也不能使用參數得地址,在實例化時,用做表達式參數的值必須是常量表達式。
2七、使用關鍵字template並指出所需類型來聲明類時,編譯器將生成類聲明的顯式實例化,聲明必須位於模板定義所在的命名空間中。
2八、C++容許部分具體化:
1 template <class T1, class T2> class Pair{}; // 通常版本 2 template <class T1> class Pair<T1, int>{}; // 部分顯式具體化版本
template後的<>中是沒有被具體化的參數,若是指定全部的類型,template後的<>將爲空,這會致使顯式具體化。
2九、若是有多個版本能夠選擇,編譯器將選擇具體化最高的版本,部分具體化特性使得可以設置各類限制。
1 template<T> class Feeb{}; 2 template<T*> class Feeb{}; // 也能夠爲指針提供特殊版原本部分具體化 3 4 template<class T1, class T2, class T3> class Trio{}; //通常版本 5 template<class T1, class T2> class Trio<T1, T2, T2>{}; // 使用T2設置T3 6 template<class T1> class Trio<T1, T1*, T1*>{}; // 使用T1的指針來設置T2和T3
30、較老的編譯器不支持模板成員,而另外一些編譯器支持模板成員,可是不支持在類外面的定義。若是支持類外定義,則必須經過做用域解析運算符指出是哪一個類的成員,而且使用嵌套模板的聲明方式。
1 template<typename T> 2 template<typename V> // 嵌套方式
3一、 對於模板類的非模板友元函數,它不是經過對象調用的,由於它不是成員函數,它能夠訪問全局對象,可使用全局指針訪問非全局對象,能夠建立本身的對象,能夠訪問獨立於對象的模板類靜態數據成員。
3二、在聲明模板類的約束模板友元函數(友元函數也是模板函數)時,須要在類中聲明具體化的友元函數,同時也須要在類外聲明而且給出友元函數的定義。
3三、除了使用typedef對模板進行重命名,C++11新增了別名。
1 template<typename T> 2 using arrtype = std::array<T,12>; 3 arrtype<int> days;
這種語法也適用於非模板,用於非模板時,它和typedef等價。