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:
- If
thisis the same object asother, do nothing (self-assignment). - Free what you currently own.
- Take
other's resources. - Leave
otherempty. - 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
- No
delete[]first:a's original 10 cells leak. - No self-assignment check: moving an object into itself (here through the reference
alias, which is easy to do by accident in real code) deletes its own buffer and then "takes" the pointer it just freed. - Not resetting
o: two objects own one buffer, so it's freed twice.
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