1. 程式人生 > >C++11新特性:移動語義和右值引用

C++11新特性:移動語義和右值引用

右值引用

傳統的C++引用(左值引用)使得識別符號關聯到左值。左值是一個表示資料的表示式(如變數名或解除引用的指標),程式可以獲得其地址。
C++11新增了右值引用。右值引用,顧名思義,可以關聯到右值,即——可以出現在賦值表示式的右邊,但不能對其應用地址運算子的值。
右值包括字面常量(C風格字串除外,它表示地址)。諸如x+y等表示式以及返回值的函式(條件是該函式返回的不是引用)。
右值引用用符號&&表示。如:

int x = 10, y = 23;
int & r = x + y;//非法,編譯器報錯
int && r2 = x + y;//合法
double
&& r3 = std::sqrt(2.0);

將右值關聯到右值引用導致該右值被儲存到特定的位置,且可以獲取該位置的地址。也就是說,雖然不能將運算子&用於12或者(x + y),但可以將其用於r1。通過將資料與特定的地址關聯,使得可以通過右值引用來訪問該資料。

但其實這不是C++11引入右值引用的主要原因。

移動語義

有些函式,表示式的執行會創建出臨時物件,在臨時物件即將被銷燬的時候,將字元,類物件等變數留在原來的地方,不去刪除它們,而是改變它們的所有權,使得它們仍然儲存在原來的地址上,但擺脫了臨時物件的身份,這種方法被稱為移動語義。

例如有一個函式返回一個臨時變數,此時編譯器做的工作就是,建立一個該臨時物件的副本,然後將臨時物件銷燬,再將該臨時物件的副本賦值給一個普通物件,然後銷燬該副本。而移動語義的做法是:直接將臨時物件的所有權交給該普通物件。

其實移動語義實際上避免了移動原始資料,而只是修改了記錄而已。

用右值引用實現移動語義

要實現移動語義,需要採取某種方式,讓編譯器知道什麼時候需要複製,什麼時候不需要。這就是右值引用發揮作用的地方。

對於類來說,可以定義一個移動建構函式。與常規的複製建構函式(使用左值引用,而且一般宣告為const)不同,移動建構函式使用右值引用作為引數,該引用關聯到右值實參。而且不同與複製建構函式可執行的深複製,移動建構函式只是調整記錄。在將所有權轉移轉移給新物件的過程中,移動建構函式可能修改其實參,這意味著右值引用引數不應是const。

下面舉例說明移動建構函式的用法及其與複製建構函式的區別:

//為了偷懶,程式只列出了兩個建構函式的具體定義並說明他們的區別
#include "iostream"
using namespace std;

class example{
 private:
    int n;      //number of elements
    char * pc;  //pointer to data
 public:
    example();
    explicit example(int k);
    example(int k, char ch);
    //copy constructor
    example(const example & f): n(f.n) {
        pc = new char[n];
        for (int i = 0; i < n; ++i)
            pc[i] = f.pc[i];
    }
    //move constructor
    example(example && f) : n(f.n) {
        pc = f.pc;
        f.pc = nullptr; //c++11
        f.n = 0;
    }
    ~example();
};

下面語句使用的是複製建構函式,它執行深複製:

example two = one;  //引用f指向左值物件one

下面語句使用的是移動建構函式:

example three (one + two);

在移動建構函式的定義中,它讓pc指向現有的資料,以獲取這些資料的所有權。此時,因為如果pc和f.pc指向相同的詩句,呼叫解構函式時將帶來麻煩,因為程式不能對同一個地址呼叫兩次delete []。所以該建構函式隨後將原來的指標設定為空指標。這也是不宣告為const的原因。
這種奪取所有權的方式成為竊取。

簡而言之,實現移動語義的兩個步驟:使用右值引用告訴編譯器什麼時候可使用移動語義和編寫移動建構函式以提供所需的行為。

所以,通過提供一個使用左值引用的複製建構函式和一個使用右值引用的建構函式,兩個建構函式將初始化分成了兩組。
使用左值物件初始化物件時,將使用複製建構函式,而使用右值物件初始化物件時,將使用移動建構函式。
程式設計師可根據需要賦予這些建構函式不同的行為。

當然,適用於建構函式的移動語義考慮也適用於賦值運算子。如

example & example::operator=(example && f) {
    if (this == &f) return *this;
    delete []pc;
    n = f.n;
    pc = f.pc;
    f.n = 0;
    f.pc = nullptr; //C++11
    return *this;
}

移動賦值運算子刪除目標物件中的原始資料,並將源物件的所有權轉讓給目標。

強制移動:

假設one two是兩個類物件,下面的語句將呼叫賦值運算子:

one = two;

但是如果不想呼叫複製運算子而是想直接使用移動語義呢?

c++11提供了標頭檔案utility中的move函式來進行轉換:

#include<utility>
one = std::move(two);

請注意:能這樣做的前提是已經定義了移動賦值運算子。

相關推薦

C++11特性移動語義引用

右值引用 傳統的C++引用(左值引用)使得識別符號關聯到左值。左值是一個表示資料的表示式(如變數名或解除引用的指標),程式可以獲得其地址。 C++11新增了右值引用。右值引用,顧名思義,可以關聯到右值,即——可以出現在賦值表示式的右邊,但不能對其應用地址運算

C++11特性function, bindlambda

function, bind和lambda:bind中使用std::ref和std::cref,bind中預設使用的拷貝,而不是引用,根據實際情況,可使用std::ref和std::cref將引數設定為引用lambda:下面我們來總結下所有出現的 lambda 引入符:[]

C++11特性總結(列舉+繼承+左右引用+變長模板)

一、列舉+斷言+異常 // C++11的一些新的特性 #include "stdafx.h" #include <cassert> using namespace std; // C++

移動語義引用

右值引用: c++的傳統引用(現在成為左值引用)使得時標符關聯到左值。左值是一個表示資料的表示式(如變數名或解除引用的指標),程式可以獲取其地址。 C++11新增了右值引用,用&&表示。右值引用可以關聯到右值,即可出現在賦值表示式的右邊,但不能對其應用地址運算子的值。右值包括

15、【C++】C++11特性Lamda表示式/可變引數模板

一、Lamda表示式     Lamda表示式是C++11中引入的一項新技術,利用Lamda表示式可以編寫內嵌的匿名函式,用以替換獨立函式或者函式物件,並且使得程式碼更可讀。是一種匿名函式,即沒有函式名的函式;Lamda函式的語法定義如下: [capture] :捕捉

C++11特性學習筆記—finaloverride關鍵字

一、final關鍵字                為什麼c++現在才提供final這個關鍵字?很奇怪。             和Java一樣,c++中的final關鍵字是用來修飾一個函式,防止這個

C++11特性Lambda函式(匿名函式)

基本的Lambda函式 我們可以這樣定義一個Lambda函式: #include <iostream> using namespace std; int main() { auto func = [] () { c

C++11 特性Lambda 表示式

或許,Lambda 表示式算得上是 C++ 11 新增特性中最激動人心的一個。這個全新的特性聽起來很深奧,但卻是很多其他語言早已提供(比如 C#)或者即將提供(比如 Java)的。簡而言之,Lambda 表示式就是用於建立匿名函式的。GCC 4.5.x 和 Micro

C++11特性應用--實現延時求(std::functionstd::bind)

說是延時求值,注意還是想搞一搞std::function和std::bind。 現在就算是補充吧,再把std::bind進行討論討論。 何為Callable Objects? 即可呼叫物件,比如函式指標、仿函式、類成員函式指標等都可稱為可呼叫物件。

C++11特性尾置返回型別

尾置返回型別是在C++11標準中新增的語法,可以用於任何函式定義中,旨在方便複雜函式的定義。尾置返回型別跟在形參列表後面並以一個->符號開頭。為了表示函式真正的返回型別跟在形參列表之後,需要在本應該出現返回型別的地方放置一個auto關鍵字。//宣告一個返回指向陣列的指

C++11中的`移動語義`與`引用`的介紹與討論

本文主要介紹了C++11中的移動語義與右值引用, 並且對其中的一些坑做了深入的討論. 在正式介紹這部分內容之前, 我們先介紹一下rule of three/five原則, 與copy-and-swap idiom最佳實踐. 本文參考了stackoverflow上的一些回答. 不能算是完全原創 rule

《深入理解C++11》筆記–引用移動語義完美轉發

上一篇:《深入理解C++11》筆記–建構函式 這篇文章介紹的了第三章中右值引用相關的內容。在介紹該內容之前,會對一些相關問題進行解釋,便於理解後面的內容。 並且,提前說明,許多編譯器會多拷貝構造和移動構造進行優化省略,這樣就看不到拷貝構造和移動構造的過程,需

C++11特性之 Move semantics(移動語義)

按值傳遞的意義是什麼? 當一個函式的引數按值傳遞時,這就會進行拷貝。當然,編譯器懂得如何去拷貝。 而對於我們自定義的型別,我們也許需要提供拷貝建構函式。 但是不得不說,拷貝的代價是昂貴的。 所以我們需要尋找一個避免不必要拷貝的方法,即C++11提供的移動

C++11 特性 移動語義

C++11支援移動語義。 一:為什麼需要移動語義和什麼是移動語義 我們先來看看C++11之前的複製過程。假設有下列程式碼: vector<string> v1(1000000);//v1存放著100W個string,假設每個string長度為1000

C++11特性——default函式deleted函式

轉自:http://blog.jobbole.com/103669/ default函式 default函式作用於類的特殊成員函式,為其自動生成預設的函式定義體,提高程式碼的執行效率。 類的特殊成員函式: 預設建構函式 解構函式 複

C++11特性之十enable_shared_from_this

 enable_shared_from_this是一個模板類,定義於標頭檔案<memory>,其原型為: template< class T > class enable_shared_from_this;        std::enable_s

C++11 特性引用轉移建構函式

問題背景 #include <iostream> usingnamespace std;   vector<int> doubleValues (const vector<int>& v)   {  

C++ 面試 C++ 11 特性引用移動

右值引用 什麼是左值,什麼是右值,簡單說左值可以賦值,右值不可以賦值。以下面程式碼為例,“ A a = getA();”該語句中a是左值,getA()的返回值是右值。 #include <iostream> class A { public

C++11特性11)- 標準庫函式beginend

遍歷陣列元素的方法假設有一個數組:inta1[]{1,2,3,4,5};遍歷陣列的所有元素,可以這樣:for(unsignedinti=0;i<sizeof(a1)/sizeof(a1[0]);++i){cout<<a1[i]<<endl;}也可

c++11特性總結boost庫的使用

來自<<深入理解c++11 c++11新特性解析與應用>> 程式碼見:https://github.com/Jeromecen/cpp11study/tree/master I、保持語言的穩定性和相容性” 總結:主要是utf8字串,虛擬函式o