深入理解C 11(九)

2021-10-13 10:39:00 字數 1913 閱讀 6357

move語義

我們知道移動語義是通過右值引用來匹配臨時值的,那麼,普通的左值是否也能借助移動語義來優化效能呢,那該怎麼做呢?事實上c++11為了解決這個問題,提供了std::move方法來將左值轉換為右值,從而方便應用移動語義。move是將物件的狀態或者所有權從乙個物件轉移到另乙個物件,只是轉移,沒有記憶體拷貝。深拷貝和move的區別如圖2-1所示。

copy和move區別

在圖2-1中,物件sourceobject中有乙個source資源物件,如果是深拷貝,要將sourceobject拷貝到destobject物件中,需要將source拷貝到destobject中;如果是move語義,要將sourceobject移動到destobject中,只需要將source資源的控制權從sourceobject轉移到destobject中,無須拷貝。

move實際上並不能移動任何東西,它唯一的功能是將乙個左值強制轉換為乙個右值引用 [1] ,使我們可以通過右值引用使用該值,以用於移動語義。強制轉換為右值的目的是為了方便實現移動構造。

這種move語義是很有用的,比如乙個物件中有一些指標資源或者動態陣列,在物件的賦值或者拷貝時就不需要拷貝這些資源了。在c++11之前拷貝建構函式和賦值函式可能要像下面這樣定義。假設乙個a物件內部有乙個資源m_ptr:

a& a::

operator=(

const a& rhs)

同樣a的拷貝建構函式也是這樣。假設這樣來使用a:

a foo()

;// foo是乙個返回值為x的函式

a a;

a =foo()

;最後一行將會發生如下操作:

·銷毀a所持有的資源。

·複製foo返回的臨時物件所擁有的資源。

·銷毀臨時物件,釋放其資源。

上面的過程是可行的,但是更有效率的辦法是直接交換a和臨時物件中的資源指標,然後讓臨時物件的析構函式去銷毀a原來擁有的資源。換句話說,當賦值操作符的右邊是右值的時候,我們希望賦值操作符被定義成下面這樣:

a& a::

operator=(

const a&& rhs)

僅僅轉移資源的所有者,將資源的擁有者改為被賦值者,這就是所謂的move語義。

再看乙個例子,假設乙個臨時容器很大,賦值給另乙個容器。

std::list< std::string> tokens;

std::list< std::string> t = std::

move

(tokens)

;如果不用std::move,拷貝的代價很大,效能較低。使用move幾乎沒有

任何代價,只是轉換了資源的所有權。實際上是將左值變成右值引用,然後

應用move語義呼叫建構函式,就避免了拷貝,提高了程式效能。當乙個物件

內部有較大的堆記憶體或者動態陣列時,很有必要寫move語義的拷貝建構函式

和賦值函式,避免無謂的深拷貝,以提高效能。事實上,c++中所有的容器都

實現了move語義,方便我們實現效能優化。

這裡也要注意對move語義的誤解,move只是轉移了資源的控制權,本質上是將左值強制轉換為右值引用,以用於move語義,避免含有資源的物件發生無謂的拷貝。move對於擁有形如對記憶體、檔案控制代碼等資源的成員的物件有效。如果是一些基本型別,比如int和char[10]陣列等,如果使用move,仍然會發生拷貝(因為沒有對應的移動建構函式),所以說move對於含資源的物件來說更有意義。

深入理解C 11(十二)

改進物件池模式 物件池對於建立開銷比較大的物件來說很有意義,為了避免重複建立開銷比較大的物件,可以通過物件池來優化。物件池的思路比較簡單,事先建立好一批物件,放到乙個集合中,每當程式需要新的物件時,就從物件池中獲取,程式用完該物件後都會把該物件歸還給物件池。這樣會避免重複建立物件,提高程式效能。物件...

深入理解C 11(十五)

emplace back減少記憶體拷貝和移動 emplace back能就地通過引數構造物件,不需要拷貝或者移動記憶體,相比push back能更好地避免記憶體的拷貝與移動,使容器插入元素的效能得到進一步提公升。在大多數情況下應該優先使用emplace back來代替push back。所有的標準庫...

深入理解C 11(十六)

unordered container無序容器 c 11增加了無序容器unordered map unordered multimap和unordered set unordered multiset,由於這些容器中的元素是不排序的,因此,比有序容器map multimap和set multiset...