1. 程式人生 > 實用技巧 >移動建構函式

移動建構函式

左值(lvalue, left value),顧名思義就是賦值符號左邊的值。準確來說, 左值是表示式(不一定是賦值表示式)後依然存在的持久物件。
右值(rvalue, right value),右邊的值,是指表示式結束後就不再存在的臨時物件。
C++11 中為了引入強大的右值引用,將右值的概念進行了進一步的劃分,分為:純右值和將亡值。
純右值 (prvalue, pure rvalue),純粹的右值,要麼是純粹的字面量,例如 10, true; 要麼是求值結果相當於字面量或匿名臨時物件,例如 1+2。非引用返回的臨時變數、運算表示式產生的臨時變數、原始字面量、Lambda 表示式都屬於純右值。

移動建構函式的作用

#include <iostream>

using namespace std;

class Base
{
public:
    Base(void) { cout << __PRETTY_FUNCTION__ << ":" << this << endl; }
    Base(const Base &) { cout << __PRETTY_FUNCTION__ << ":" << this << endl; }
    Base(Base &&) { cout << __PRETTY_FUNCTION__ << ":" << this << endl; }

    Base &operator=(const Base &)
    {
        cout << __PRETTY_FUNCTION__ << ":" << this << endl;
        return *this;
    }

    ~Base() { cout << __PRETTY_FUNCTION__ << ":" << this << endl; }

private:
};

Base func()
{
    return Base();
}

int main(int argc, char **argv)
{
    auto bb = func();

    return 0;
}

注意,目前多數編譯器都會對程式中發生的拷貝操作進行優化,因此直接編譯執行此程式時,看到的往往是優化後的輸出結果:

在 Linux 上使用 g++ demo.cpp -fno-elide-constructors,就可以看到完整的輸出結果:

可以看出函式 func 在沒有優化時候流程是這樣的:

  • 執行Base() 預設建構函式,構造一個臨時的匿名物件
  • 拷貝匿名物件到返回值,並銷燬匿名變數
  • 執行 bb = 返回值,也就是執行拷貝建構函式,然後銷燬返回值
    這之間多次呼叫了建構函式。

移動建構函式

      ...
    Base(Base &&) { cout << __PRETTY_FUNCTION__ << ":" << this << endl; }
      ...


可以看出,新增移動建構函式後執行了移動建構函式Base::Base(Base&&)。減少了多次呼叫建構函式的開銷。(移動語義與完美轉發僅在編譯階段起作用,執行期間不執行任何程式碼)。

移動語義與完美轉發

C++11朝碼夕解: move和forward
C++11 std::move和std::forward
c++11 中的 move 與 forward
[CPP] 左值 lvalue,右值 rvalue 和移動語義 std::move
[c++11]我理解的右值引用、移動語義和完美轉發