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

來源:互聯網
上載者:User

標籤:c++11   移動語意   

按值傳遞的意義是什嗎?
當一個函數的參數按值傳遞時,這就會進行拷貝。當然,編譯器懂得如何去拷貝。
而對於我們自訂的類型,我們也許需要提供拷貝建構函式。

但是不得不說,拷貝的代價是昂貴的。

所以我們需要尋找一個避免不必要拷貝的方法,即C++11提供的移動語意。
上一篇部落格中有一個句話用到了:

#include <iostream>void f(int& i) { std::cout << "lvalue ref: " << i << "\n"; }void f(int&& i) { std::cout << "rvalue ref: " << i << "\n"; }int main(){    int i = 77;    f(i);    // lvalue ref called    f(99);   // rvalue ref called    f(std::move(i));  // 稍後介紹    return 0;}

實際上,右值引用注意用於建立移動建構函式和移動賦值運算。

移動建構函式類似於拷貝建構函式,把類的執行個體對象作為參數,並建立一個新的執行個體對象。
但是 移動建構函式可以避免記憶體的重新分配,因為我們知道右值引用提供了一個暫時的對象,而不是進行copy,所以我們可以進行移動。

換言之,在設計到關於臨時對象時,右值引用和移動語意允許我們避免不必要的拷貝。我們不想拷貝將要消失的臨時對象,所以這個臨時對象的資源可以被我們用作於其他的對象。

右值就是典型的臨時變數,並且他們可以被修改。如果我們知道一個函數的參數是一個右值,我們可以把它當做一個臨時儲存。這就意味著我們要移動而不是拷貝右值參數的內容。這就會節省很多的空間。

說多無語,看代碼:

#include <iostream>#include <algorithm>class A{public:    // Simple constructor that initializes the resource.    explicit A(size_t length)        : mLength(length), mData(new int[length])    {        std::cout << "A(size_t). length = "        << mLength << "." << std::endl;    }    // Destructor.    ~A()    {    std::cout << "~A(). length = " << mLength << ".";    if (mData != NULL) {            std::cout << " Deleting resource.";        delete[] mData;  // Delete the resource.    }    std::cout << std::endl;    }    // Copy constructor.    A(const A& other)        : mLength(other.mLength), mData(new int[other.mLength])    {    std::cout << "A(const A&). length = "        << other.mLength << ". Copying resource." << std::endl;    std::copy(other.mData, other.mData + mLength, mData);    }    // Copy assignment operator.    A& operator=(const A& other)    {    std::cout << "operator=(const A&). length = "             << other.mLength << ". Copying resource." << std::endl;    if (this != &other) {        delete[] mData;  // Free the existing resource.        mLength = other.mLength;            mData = new int[mLength];            std::copy(other.mData, other.mData + mLength, mData);    }    return *this;    }    // Move constructor.    A(A&& other) : mData(NULL), mLength(0)    {        std::cout << "A(A&&). length = "              << other.mLength << ". Moving resource.\n";        // Copy the data pointer and its length from the         // source object.        mData = other.mData;        mLength = other.mLength;        // Release the data pointer from the source object so that        // the destructor does not free the memory multiple times.        other.mData = NULL;        other.mLength = 0;    }    // Move assignment operator.    A& operator=(A&& other)    {        std::cout << "operator=(A&&). length = "              << other.mLength << "." << std::endl;        if (this != &other) {          // Free the existing resource.          delete[] mData;          // Copy the data pointer and its length from the           // source object.          mData = other.mData;          mLength = other.mLength;          // Release the data pointer from the source object so that          // the destructor does not free the memory multiple times.          other.mData = NULL;          other.mLength = 0;       }       return *this;    }    // Retrieves the length of the data resource.    size_t Length() const    {        return mLength;    }private:    size_t mLength; // The length of the resource.    int* mData;     // The resource.};

移動建構函式
文法:

A(A&& other) noexcept    // C++11 - specifying non-exception throwing functions{  mData =  other.mData;  // shallow copy or referential copy  other.mData = nullptr;}

最主要的是沒有用到新的資源,是移動而不是拷貝。
假設一個地址指向了一個有一百萬個int元素的數組,使用move建構函式,我們沒有創造什麼,所以代價很低。

// Move constructor.A(A&& other) : mData(NULL), mLength(0){    // Copy the data pointer and its length from the     // source object.    mData = other.mData;    mLength = other.mLength;    // Release the data pointer from the source object so that    // the destructor does not free the memory multiple times.    other.mData = NULL;    other.mLength = 0;}

移動比拷貝更快!!!

移動賦值運算子
文法:

A& operator=(A&& other) noexcept{  mData =  other.mData;  other.mData = nullptr;  return *this;}

工作流程這樣的:Google上這麼說的:

Release any resources that *this currently owns.
Pilfer other’s resource.
Set other to a default state.
Return *this.

// Move assignment operator.A& operator=(A&& other){    std::cout << "operator=(A&&). length = "              << other.mLength << "." << std::endl;    if (this != &other) {      // Free the existing resource.      delete[] mData;      // Copy the data pointer and its length from the       // source object.      mData = other.mData;      mLength = other.mLength;      // Release the data pointer from the source object so that      // the destructor does not free the memory multiple times.      other.mData = NULL;      other.mLength = 0;   }   return *this;}

讓我們看幾個move帶來的好處吧!
vector眾所周知,C++11後對vector也進行了一些最佳化。例如vector::push_back()被定義為了兩種版本的重載,一個是cosnt T&左值作為參數,一個是T&&右值作為參數。例如下面的代碼:

std::vector<A> v;v.push_back(A(25));v.push_back(A(75));

上面兩個push_back()都會調用push_back(T&&)版本,因為他們的參數為右值。這樣提高了效率。

而 當參數為左值的時候,會調用push_back(const T&) 。

#include <vector>int main(){    std::vector<A> v;    A aObj(25);       // lvalue    v.push_back(aObj);  // push_back(const T&)}

但事實我們可以使用 static_cast進行強制:

// calls push_back(T&&)v.push_back(static_cast<A&&>(aObj));

我們可以使用std::move完成上面的任務:

v.push_back(std::move(aObj));  //calls push_back(T&&)

似乎push_back(T&&)永遠是最佳選擇,但是一定要記住:
push_back(T&&) 使得參數為空白。如果我們想要保留參數的值,我們這個時候需要使用拷貝,而不是移動。

最後寫一個例子,看看如何使用move來交換兩個對象:

#include <iostream>using namespace std;class A{  public:    // constructor    explicit A(size_t length)        : mLength(length), mData(new int[length]) {}    // move constructor    A(A&& other)    {      mData = other.mData;      mLength = other.mLength;      other.mData = nullptr;      other.mLength = 0;    }    // move assignment    A& operator=(A&& other) noexcept    {      mData =  other.mData;      mLength = other.mLength;      other.mData = nullptr;      other.mLength = 0;      return *this;    }    size_t getLength() { return mLength; }    void swap(A& other)    {      A temp = move(other);      other = move(*this);      *this = move(temp);    }    int* get_mData() { return mData; }  private:    int *mData;    size_t mLength;};int main(){  A a(11), b(22);  cout << a.getLength() << ‘ ‘ << b.getLength() << endl;  cout << a.get_mData() << ‘ ‘ << b.get_mData() << endl;  swap(a,b);  cout << a.getLength() << ‘ ‘ << b.getLength() << endl;  cout << a.get_mData() << ‘ ‘ << b.get_mData() << endl;  return 0;}

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.