侯捷-C++2.0


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM