我們還是從大象與冰箱的故事說起。大象裝入冰箱是乙個很麻煩的過程,因為大象很大,假設可以裝入冰箱。那麼如何把大象裝入另外乙個冰箱。
c++有2種方案:
我們先利用量子技術分析冰箱a中大象的構造資訊,然後在冰箱b利用量子技術利用碳,氫,氧、氮、……等元素合稱,如果你願意還可以利用量子技術將冰箱a中的大象瞬間湮滅。
我們直接把冰箱a砸掉,然後再原地套上乙個冰箱b(假設冰箱可以拼裝),顯然冰箱a不存在了。
上圖:
方案一
方案二:
//類的定義
class
vector
//預設建構函式
vector(size_t len, int value) : size(len)//帶引數的建構函式
vector(const
vector&);//拷貝建構函式
vector(vector&& v);//移動建構函式
void print()//列印陣列
~vector()//析構函式
private:
size_t size; //陣列長度
int *pdata; //首位址
};vector::vector(const
vector& v)
vector::vector(vector&& v) :size(v.size), pdata(v.pdata)
int main()
**很簡單,乙個類,主要看移動建構函式和拷貝建構函式,為了體現區別,我們特意在構造的時候申請500000個的大小。
執行上面的**,會呼叫拷貝建構函式,雖然我們也編寫了移動建構函式,但是,處於函式匹配的原則,v2(v1)
中的引數v1是左值,所以會採用拷貝建構函式,拷貝構造函式呼叫後,原物件v1依然存在。
為了呼叫移動建構函式,我們需要傳遞乙個與原型匹配的右值引數,而標準庫中提供了乙個左值轉右值的函式std::move()
,所以我們將剛才的語句重寫為:
vector v2(move(v1));
那麼就會呼叫移動建構函式。
我們可以回想之前大象裝冰箱的例子,方案一就是對於拷貝建構函式,我們進行深拷貝,獲得的是大象的構造資訊,然後自己造乙個大象,這個過程有時候會很麻煩,因為實際使用中,我們需要拷貝的也許不是一頭大象,而是整個動物園。然而在c++11以前,基本上都是這麼幹的。小孩子都知道,需要乙個玩具車,不是先去分析玩具車,然後造出來,而是直接把喜歡的那個買回來,也就是商店的那個玩具車直接變到我家裡來。同樣,移動建構函式也是類似,假如我不需要原來的資料儲存,我只需要這個資料,為什麼一定要構造乙個副本,而不是直接拿過來呢?這樣豈不是會省事很多?
也就是說,移動建構函式是反客為主,直接掌管原資料的所有權,並將原來的擁有者銷毀掉。這樣避免資料的複製,所以效率會提高。
分兩次執行上面的**,我們利用vs自帶的效能分析工具來分析一下:
首先是拷貝建構函式:
然後是移動建構函式:
我們只需要關注main下面的資料,對比分析發現在拷貝建構函式中,主要是拷貝建構函式和析構函式,因為是深拷貝,所以需要釋放v1,v2所包含的記憶體空間,而且在呼叫拷貝建構函式時也需要開闢一塊較大的記憶體空間。而在移動建構函式版本中,主要是建構函式和移動建構函式操作,外部**所佔比例相對於拷貝建構函式來說有所上公升,說明移動建構函式較為高效,所需cpu資源較少。
關於效能分析,可能解釋不一定準確。若有錯誤,歡迎指出。
右值引用與移動建構函式 移動賦值
有一陣子沒看c 了,翻開c primer又陌生了一些,想了想引用,於是乎來看了下右值引用。int a 5 int b a 這是左值引用,若我們這樣修改 int a 5 int b a 1 編譯器會報錯 非常量引用的初始值必須為左值。也就是右邊的a 1是常量,常量給非常量引用賦值就報錯。我們可以這樣修...
C 右值引用與移動構造
c 中提供了兩種引用方式。左值引用與右值引用。其中右值引用是c 11的新標準新增的內容。所謂的右值引用就是必須繫結到右值的引用。在介紹之前,先說明一下c 中的左值和右值的規定。實際上,最早在c語言中就有了左值和右值之分。最初的左值即指在賦值號左邊的變數,右值指在賦值號右邊的變數。隨著c語言的發展和c...
右值引用 移動建構函式和move
左值和右值判斷 1 可位於賦值號 左側的表示式就是左值 反之,只能位於賦值號右側的表示式就是右值。2 有名稱的 可以獲取到儲存位址的表示式即為左值 反之則是右值。例如 int i 10 10 i 錯誤,10為右值,不能當左值用 int j 20 j i i和j都是左值,但是i可以當右值用 以上面定義...