移動建構函式

假設我們有這段程式碼。

class A {
public:
    int a;
    int b;
       
    A(const A &other) {
        this->a = other.a;
        this->b = other.b;
    }
};

要建立一個複製建構函式,即建立一個複製物件並建立一個新物件的函式,我們通常會選擇上面顯示的語法,我們將有一個 A 的建構函式,它引用另一個型別為 A 的物件,我們將在方法內手動複製物件。

或者,我們可以編寫 A(const A &) = default;,它會自動複製所有成員,並使用其複製建構函式。

但是,要建立移動建構函式,我們將使用右值引用而不是左值引用,就像這裡一樣。

class Wallet {
public:
    int nrOfDollars;
    
    Wallet() = default; //default ctor

    Wallet(Wallet &&other) {
        this->nrOfDollars = other.nrOfDollars;
        other.nrOfDollars = 0;
    }
};

請注意我們將舊值設定為 zero。預設移動建構函式(Wallet(Wallet&&) = default;)複製 nrOfDollars 的值,因為它是 POD。

由於移動語義被設計為允許從原始例項竊取狀態,因此考慮在此竊取之後原始例項應該是什麼樣子是很重要的。在這種情況下,如果我們不將值更改為零,我們就會將美元數量增加一倍。

Wallet a;
a.nrOfDollars = 1;
Wallet b (std::move(a)); //calling B(B&& other);
std::cout << a.nrOfDollars << std::endl; //0
std::cout << b.nrOfDollars << std::endl; //1

因此,我們移動構造了一箇舊物件。

雖然上面是一個簡單的例子,但它顯示了移動建構函式的目的。它在更復雜的情況下變得更有用,例如涉及資源管理時。

    // Manages operations involving a specified type.
    // Owns a helper on the heap, and one in its memory (presumably on the stack).
    // Both helpers are DefaultConstructible, CopyConstructible, and MoveConstructible.
    template<typename T,
             template<typename> typename HeapHelper,
             template<typename> typename StackHelper>
    class OperationsManager {
        using MyType = OperationsManager<T, HeapHelper, StackHelper>;

        HeapHelper<T>* h_helper;
        StackHelper<T> s_helper;
        // ...

      public:
        // Default constructor & Rule of Five.
        OperationsManager() : h_helper(new HeapHelper<T>) {}
        OperationsManager(const MyType& other)
          : h_helper(new HeapHelper<T>(*other.h_helper)), s_helper(other.s_helper) {}
        MyType& operator=(MyType copy) {
            swap(*this, copy);
            return *this;
        }
        ~OperationsManager() {
            if (h_helper) { delete h_helper; }
        }

        // Move constructor (without swap()).
        // Takes other's HeapHelper<T>*.
        // Takes other's StackHelper<T>, by forcing the use of StackHelper<T>'s move constructor.
        // Replaces other's HeapHelper<T>* with nullptr, to keep other from deleting our shiny
        //  new helper when it's destroyed.
        OperationsManager(MyType&& other) noexcept
          : h_helper(other.h_helper),
            s_helper(std::move(other.s_helper)) {
            other.h_helper = nullptr;
        }

        // Move constructor (with swap()).
        // Places our members in the condition we want other's to be in, then switches members
        //  with other.
        // OperationsManager(MyType&& other) noexcept : h_helper(nullptr) {
        //     swap(*this, other);
        // }

        // Copy/move helper.
        friend void swap(MyType& left, MyType& right) noexcept {
            std::swap(left.h_helper, right.h_helper);
            std::swap(left.s_helper, right.s_helper);
        }
    };