C++ 2.0的內容分為2部分講解:
一, 新的語法;
二, 標准庫中新的內容;
一、 新的語法
1. 聲明一個類時,C++會默認生成big five成員函數。
默認構造函數,默認析構函數,默認拷貝構造,默認賦值構造,默認移動構造(移動拷貝構造,移動賦值構造)。
默認構造函數的作用:調用當前類的父類構造函數。
默認拷貝構造函數的作用:將非靜態的成員變量逐個進行復制。若成員變量中存在指針,執行的是淺拷貝。因此,定義一個新類時,若成員變量中村子指針,則需要重新定義big three函數(默認拷貝構造,默認賦值構造,默認析構),將淺拷貝改為深拷貝。
2. 一般情況下,構造函數都是public權限。但是,若構造函數聲明為private權限,則只有編譯器有權限調用該構造函數,任何用戶都無權限調用。
3. 關鍵字 decltype
作用:let the compiler find out the type of an expression.
應用:a) 聲明返回類型;b) 元編程; c) 獲取lambda函數的類型;
a)利用decltype提供一種返回類型的聲明方法。
template <typename T1, typename T2>
auto add(T1 x, T2 y) -> decltype(x+y);
4. lambdas 函數
lambdas函數可以視為函數對象(function object),其對應關系可以用如下兩個例子展示。
例子1
例子2
5. 可變模板
變化的是模板參數。參數的數量和類型都會變化。
利用參數個數逐一遞減的特性,實現遞歸函數調用。
可變模板例子 1)
函數1是遞歸終止條件;
函數2和函數3可以並存,但是編譯器會優先使用更為特化的函數2;
可變模板例子 3)
利用可變模板實現取最大值的功能。
右下角的代碼,結構體 _Iter_less_iter,成員函數重載()。_Iter_less_iter(),是調用了默認構造函數?還是調用了重載函數()?
侯捷介紹:_Iter_less_iter 重載了(),因此對象可以像函數一樣被調用。
可變模板例子 6)
tuple的構造函數利用可變模板實現遞歸繼承,從而完成tuple對象的初始化。
構造函數中,在初始化列表中,調用父類構造函數 inherited(vtail...)。
使用private繼承,其目的在於僅僅使用父類對象的內存空間。
存疑地方,typename的使用 typename Head::type head() { return m_head; }
可變模板例子 7)
利用可變模板實現tuple類型的對象初始化。遞歸復合的內存示意圖如右上角所示,x<T1, T2, T3>中包含x<T2, T3> ,,,
二、 標准庫新的內容
本節的教學目標:自己設計一個 move aware class,可以執行move操作。
右值引用 rvalue references
作用:右值引用可以優化一些不必要的拷貝操作,極大提升代碼的效率。
1) 臨時對象就是一種右值;
2) 右值只能出現在等號右邊;
編譯器可以識別函數名為函數所在的內存地址起點,即函數所在的地址。
注意:如下代碼段中,foo為函數名;foo() 代表函數返回的東西,是一個右值,右值不可尋址。
如下例子,通過右值引用避免拷貝操作。
需要滿足如下條件:
1)調用端告訴編譯器,這是一個右值。可以是臨時對象 或者 使用std::move()將左值轉換為右值;
2)被調用端寫出一個專門處理rvalue的移動賦值函數,例如 c.insert(..., &&x)。形參中的右值引用要求元素類型必須要有對應移動構造函數(與之相對應,拷貝構造函數都是深拷貝操作)。
上圖代碼中, c,c1,c2都是 std::vector<MyString> 類型。
其中 M c1(c); 執行深拷貝; M c2(std::move(c1)); 執行vector中3個指針的交換(swap)。
3個指針包括,vector的內存首指針,內存尾指針和數據尾指針。
被調用端
如圖所示,G4.9 是 C++ 2.0。其中,vector.insert()添加了專門處理右值引用的版本(move aware)。
C++ 2.0 中新版本的 vector.insert(..., && x) 要求vector中存儲的元素要有move aware的構造函數,包括移動構造和移動賦值。這樣的元素類型才能進行移動操作,節省拷貝操作。
完美傳遞 和 不完美傳遞
如下 forward(2); 和 forward(move(a)); 所示,輸入右值參數,在forward() 函數內部調用proces() 函數時,卻變成了左值。這是不完美的傳遞。
C++2.0 提供完美傳遞 std::forward<>(),可以保持參數的特性,包括modifiable,const,lvalue 和 rvalue。
設計一個move aware class, MyString。
MyString 的成員中有指針。針對指針變量,移動構造相比拷貝構造節省了拷貝構造操作。
拷貝構造函數執行深拷貝;移動構造函數執行淺拷貝,同時把臨時對象的指針置為nullptr。
在析構函數中,判斷臨時對象的指針 _data 是否為空?若不為空,則進行delete操作。
標准庫的數據結構
紅色方框標識出C++2.0新的數據結構
hashtable
籃子的大小 A=53,是一個質數。一個元素,對應一個hash code,它在hash表中存放位置 p 的計算方法為:
hash code % A = p
舉個例子,55 % 53 = 2.
當元素數量大於籃子數量時,擴充籃子數量至 2A 左右(取2A附近的最近質數),然后重新計算每個元素在hashtable中的存儲位置。
在實際情況中,hashtable中存放的是對象。因此,要利用 hash function 計算每個對象相應的 hash code。
如下所示,計算一些基本數據類型的 hash code。hash code的特點要夠雜夠亂夠隨機,可以避免在 hashtable 中存放的沖突。
對於 hash<int>()(123) ,第一個小括號表示創建一個臨時對象,是一個 function object。第二個小括號表示調用這個function object,其行為類似於調用函數,傳入參數123.
上述代碼的底層實現原理如下,先創建一個泛化的 hash 結構體,再針對每一種基本數據類型進行特化。
利用hash function計算字符串的hash code