Step 4 of 8
The copy problem
RAII classes have a hidden trap. When you copy an object, C++ by default copies each member. For a class holding a raw pointer, that copies the pointer, not the data it points to. Now two objects point at the same array:
a.data_ ──┐
├──> [ 1 2 3 ]
b.data_ ──┘
Changing b changes a. Worse, when both are destroyed, both destructors delete[] the same array: a double free, which crashes or corrupts memory.
The fix: a deep copy
Write a copy constructor, the constructor that builds a new object from an existing one of the same type. It takes a const reference to the original and allocates new memory with the same contents:
#include <iostream>
#include <cstring>
class Name {
public:
explicit Name(const char* s) : text_(new char[std::strlen(s) + 1]) { std::strcpy(text_, s); }
Name(const Name& other) : text_(new char[std::strlen(other.text_) + 1]) {
std::strcpy(text_, other.text_);
}
~Name() { delete[] text_; }
Name& operator=(const Name&) = delete;
void upper_first() { text_[0] = text_[0] - 'a' + 'A'; }
const char* c_str() const { return text_; }
private:
char* text_;
};
int main() {
Name a("ada");
Name b = a;
b.upper_first();
std::cout << a.c_str() << " " << b.c_str() << "\n";
}
ada Ada
Name b = a; calls the copy constructor, which gives b its own characters. Changing b doesn't affect a, and each destructor frees its own memory.
The rule of three
If a class needs a custom destructor, it almost certainly needs a custom copy constructor and copy assignment operator too, because all three deal with the same owned resource. (Here copy assignment is simply forbidden with = delete; the next steps cover both options.)
Your turn: add the deep-copy constructor. (Copy assignment is provided.)