Copy, move and operator overloading
When the copy constructor, copy assignment and move constructor run, the rule of three, and how to overload operators without surprises.
If a class owns a resource such as heap memory or a file handle, the compiler-generated copy does the wrong thing, and interviewers know it. This lesson shows exactly which special function runs on each line and how to write them, then covers operator overloading, which appears in the same questions.
Which function runs when
class Buffer {
char* data;
std::size_t n;
public:
explicit Buffer(std::size_t size) : data(new char[size]), n(size) { std::cout << "ctor\n"; }
Buffer(const Buffer& other) : data(new char[other.n]), n(other.n) {
std::memcpy(data, other.data, n);
std::cout << "copy\n";
}
Buffer& operator=(const Buffer& other) {
if (this != &other) {
char* fresh = new char[other.n];
std::memcpy(fresh, other.data, other.n);
delete[] data;
data = fresh;
n = other.n;
}
std::cout << "assign\n";
return *this;
}
Buffer(Buffer&& other) noexcept : data(other.data), n(other.n) {
other.data = nullptr;
other.n = 0;
std::cout << "move\n";
}
~Buffer() { delete[] data; }
};
Buffer a(16); // ctor
Buffer b = a; // copy (initialisation, not assignment)
b = a; // assign
Buffer d = std::move(a); // move; a is now emptyBuffer b = a; calls the copy constructor because b is being created. b = a; calls copy assignment because b already exists. The move constructor steals the pointer and must leave the source in a valid, destructible state.
The rule of three and five
If you write any one of the destructor, copy constructor or copy assignment, you almost certainly need all three. Add the move constructor and move assignment to make the class cheap to return and store in containers. Better still, hold resources in std::string or std::unique_ptr so the compiler can generate all five correctly.
Operator overloading
struct Money {
long paise;
Money operator+(const Money& o) const { return Money{paise + o.paise}; }
bool operator<(const Money& o) const { return paise < o.paise; }
};
std::ostream& operator<<(std::ostream& os, const Money& m) {
return os << m.paise << " paise";
}
Money total = Money{1050} + Money{250};
std::cout << total << "\n"; // 1300 paiseArithmetic and comparison operators are const members that take a const reference and return a new value. operator<< must be a free function because its left operand is the stream, and it returns the stream so calls can chain. Defining operator< is what lets std::sort and std::map work with your type.
Common mistakes and a practice habit
- Relying on the default copy for a class that owns a raw pointer, which leads to a double
delete. - Forgetting
return *this;inoperator=. - Using the moved-from object as if it still held its data.
- Making
operator<<a member function.
Practice habit: Add a print statement to each special member function of a small class, then write ten lines that create, copy, assign, pass and return it. Predict the output before running. Repeat until you can read any such snippet and list the calls in order.
More in Programming