C/C++ Arena

Step 3 of 7

Move assignment and the rule of five

The move constructor makes a new object from a dying one. Move assignment, a = std::move(b);, is harder, because a already exists and may already own something. The recipe:

  1. If this is the same object as other, do nothing (self-assignment).
  2. Free what you currently own.
  3. Take other's resources.
  4. Leave other empty.
  5. Return *this, so assignments can chain.
#include <iostream>
#include <utility>

class Grid {
public:
    explicit Grid(int n) : cells_(new int[n]()), n_(n) {}
    ~Grid() { delete[] cells_; }
    Grid(Grid&& o) noexcept : cells_(o.cells_), n_(o.n_) { o.cells_ = nullptr; o.n_ = 0; }

    Grid& operator=(Grid&& o) noexcept {
        if (this != &o) {
            delete[] cells_;                 // release the old buffer
            cells_ = o.cells_;
            n_ = o.n_;
            o.cells_ = nullptr;
            o.n_ = 0;
        }
        return *this;
    }

    int size() const { return n_; }

private:
    int* cells_;
    int n_;
};

int main() {
    Grid a(10), b(3);
    a = std::move(b);                        // a frees its 10 cells, takes b's 3
    std::cout << a.size() << " " << b.size() << "\n";
    Grid& alias = a;                         // another name for a
    a = std::move(alias);                    // self-move: guarded, nothing breaks
    std::cout << a.size() << "\n";
}
3 0
3

What goes wrong without each step

The rule of five, and the rule of zero

The five special members are the destructor, copy constructor, copy assignment, move constructor and move assignment. If you write any of them, you're managing a resource by hand, so think about all five.

Better still is the rule of zero: hold resources in members that already manage themselves, such as std::vector<int> or std::unique_ptr<int[]>. Then the compiler generates correct versions of all five, and you write none. Hand-written moves are worth learning so you understand what those types do for you.

Your turn: add move assignment.

Previous: Write a move constructor Next: Return by value is free