C++ move semantics

move semantics

C++11中引入了move semantics,它是基於rvalue reference來優化物件搬移效率的一種機制。

在一個自定義的類別中,如果我們希望使用move semantics,就必須為它定義兩個成員函數:move constructor及move assignment operator。

move constructor

move constructor的核心理念是:

  1. 把temporary object中的資料給搬到新物件中
  2. 將temporary object還原到一個valid state(可以想成0或NULL)

TensorRT/samples/common/buffers.h中:

//move constructor
//此處的GenericBuffer&&就是rvalue reference
GenericBuffer(GenericBuffer&& buf)
    //獲取temporary object的成員屬性
    : mSize(buf.mSize)
    , mCapacity(buf.mCapacity)
    , mType(buf.mType)
    , mBuffer(buf.mBuffer)
{
    //將temp object還原到一個valid state
    buf.mSize = 0;
    buf.mCapacity = 0;
    buf.mType = nvinfer1::DataType::kFLOAT;
    buf.mBuffer = nullptr;
}

注意此處move constructorGenericBuffer(GenericBuffer&& buf)的輸入是一個rvalue reference。

第一步先將buf的屬性給搬到新物件中,第二步則是將傳入的參數buf清空,注意因為這裡是右值引用,所以我們才可以修改buf

move assignment operator

move assignment operator裡包含三步驟,相較於move constructor多了一步,即一開始需要將其現在的資料給清空:

  1. 把現有資料清空
  2. 把temporary object中的資料給搬到新物件中
  3. 將temporary object還原到一個valid state(可以想成0或NULL)

同樣來自TensorRT/samples/common/buffers.h

//move assignment operator
GenericBuffer& operator=(GenericBuffer&& buf)
{
    if (this != &buf)
    {
        //清理現有的資料
        freeFn(mBuffer);
        //從temporary object中獲取資料
        mSize = buf.mSize;
        mCapacity = buf.mCapacity;
        mType = buf.mType;
        mBuffer = buf.mBuffer;
        // Reset buf.
        //將temporary state還原到一個valid state
        //注意!在使用move assignment operator時等號右邊的GenericBuffer物件會被reset!
        buf.mSize = 0;
        buf.mCapacity = 0;
        buf.mBuffer = nullptr;
    }
    return *this;
}

vector::emplace_back(move(unique_ptr))

同樣來自TensorRT/samples/common/buffers.h

//std::vector<std::unique_ptr<ManagedBuffer>> mManagedBuffers; //!< The vector of pointers to managed buffers
//std::unique_ptr<ManagedBuffer> manBuf{new ManagedBuffer()};
mManagedBuffers.emplace_back(std::move(manBuf));

由於manBuf是unique_ptr,所以它不能被複製。
如果使用 v.emplace_back(manBuf)的寫法,根據std::vector::emplace_back with lvalue expression它實際上會複製一份manBuf再將它放入向量中,所以這肯定是會報錯的。

move semantics的存在讓我們可以移交物件的所有權而不需要另外複製一份。
為了使用move semantics,我們必須先將manBuflvalue轉換為rvaluervalue默認使用move semantics),而這就是std::move(manBuf)在這裡的意義。以下是std::move的定義:

template <class T>
typename remove_reference<T>::type&& move (T&& arg) noexcept;

它的作用是回傳其參數arg的rvalue reference。

vectoremplace_back的參數正好是rvalue reference:

template <class... Args>
  void emplace_back (Args&&... args);

兩者正可遙相呼應。

參考連結

C++ rvalue references and move semantics for beginners

Why is move necessary with emplace_back in this example?

Pushing an object with unique_ptr into vector in C++

std::vector::emplace_back with lvalue expression

std::move

std::vector::emplace_back

发布了90 篇原创文章 · 获赞 9 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/keineahnung2345/article/details/104074631
今日推荐