Step 2 of 7
Write a move constructor
How does a class become movable? It provides a move constructor: a constructor that takes an rvalue reference, written T&&. An && parameter only binds to rvalues (temporaries and std::moved values), so this constructor runs exactly when stealing is allowed.
The recipe for a class that owns a raw resource:
- Copy the pointer (and sizes) from
otherinto the new object. - Set
other's pointer tonullptr(and sizes to 0), so its destructor won't free what you just took.
#include <cstring>
#include <iostream>
#include <utility>
class Text {
public:
explicit Text(const char* s) : len_(std::strlen(s)), buf_(new char[len_ + 1]) {
std::memcpy(buf_, s, len_ + 1);
}
~Text() { delete[] buf_; }
Text(const Text&) = delete;
Text& operator=(const Text&) = delete;
Text(Text&& other) noexcept : len_(other.len_), buf_(other.buf_) {
other.buf_ = nullptr; // other no longer owns the buffer
other.len_ = 0;
std::cout << "moved\n";
}
const char* c_str() const { return buf_ ? buf_ : "(empty)"; }
private:
std::size_t len_;
char* buf_;
};
int main() {
Text a("hello");
Text b(std::move(a)); // runs the move constructor
std::cout << b.c_str() << " / " << a.c_str() << "\n";
} // both destructors run; delete[] nullptr is harmless
moved
hello / (empty)
Why each part matters
- Without step 2, both
aandbwould point at the same buffer, and both destructors woulddelete[]it: a double free. delete[] nullptrdoes nothing, so the moved-from object's destructor is safe.noexceptpromises the move can't fail.std::vectorchecks this when it grows: it only moves elements into the new buffer if the move isnoexcept, otherwise it falls back to copying. Moves that just shuffle pointers can't fail, so always mark them.- The member initializer list reads
other's members before the body resets them.
Your turn: write the move constructor for Buffer.
Previous: Copies are expensive Next: Move assignment and the rule of five