Copy constructor and copy assignment — Rule of Three
WHAT are these three things?
The key distinction:
MyString a = b; // copy CONSTRUCTOR (a is being born)
MyString c;
c = b; // copy ASSIGNMENT (c already exists)WHY does the compiler-generated version break? (Shallow vs Deep)

Shallow copy aftermath:
a.data ──┐
├──► [ "hi\0" ] one buffer, two owners
b.data ──┘
When a and b both destruct: delete[] (same ptr) runs twice → undefined behavior. Also, writing through a silently corrupts b (aliasing).
HOW to write them — deriving a safe MyString from scratch
We need a class that owns a char*. Let's build each member by reasoning about what must happen.
#include <cstring> // strlen, strcpy
class MyString {
char* data; // owned resource
std::size_t len;
public:
// --- normal constructor ---
MyString(const char* s = "") {
len = std::strlen(s);
data = new char[len + 1]; // +1 for the '\0'
std::strcpy(data, s);
}Why len+1? strings need a null terminator slot.
// --- (1) Destructor: how the resource dies ---
~MyString() {
delete[] data; // [] because we did new[]
}Why delete[]? Array-new must be paired with array-delete, else UB.
// --- (2) Copy constructor: born as a copy ---
MyString(const MyString& other) {
len = other.len;
data = new char[len + 1]; // OWN memory — deep copy
std::strcpy(data, other.data);
}Why allocate fresh? So the new object owns an independent buffer; destroying one won't affect the other.
// --- (3) Copy assignment: overwrite an existing object ---
MyString& operator=(const MyString& other) {
if (this == &other) return *this; // guard self-assignment
delete[] data; // free OLD resource
len = other.len;
data = new char[len + 1]; // allocate new
std::strcpy(data, other.data);
return *this; // for chaining a=b=c
}
};WHY each line of operator= exists (steel-manned)
Worked examples
Modern footnote: Rule of Five & Rule of Zero
Active recall
What three members does the Rule of Three concern?
Why does the Rule of Three exist?
Distinguish copy constructor vs copy assignment by trigger.
What is a shallow copy vs deep copy?
Two bugs from default shallow copy of a pointer?
Why must copy assignment guard against self-assignment?
Why must operator= free the old resource but the copy ctor must not?
What signature is conventional for copy assignment?
T& operator=(const T& other) — returns reference for chaining.What does copy-and-swap buy you?
Why pair new[] with delete[]?
What is the Rule of Zero?
Recall Feynman: explain to a 12-year-old
Imagine each object has a toy box (memory) and a note with the box's address. The lazy copy just photocopies the note, so now two kids point at the same box. When playtime ends, both kids try to throw the box away — but you can't throw away the same box twice, so everything breaks! The Rule of Three says: if you gave a kid a box, you must teach them three things — how to clean up the box, how to make their own copy of the box when born, and how to swap their box for a copy later. Do all three or none.
Connections
- Destructors and RAII
- Move semantics and Rule of Five
- Rule of Zero and smart pointers
- Deep vs shallow copy
- Operator overloading
- Dynamic memory new and delete
- Self-assignment and exception safety
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Dekho, jab tumhare class ke andar koi raw pointer hota hai jo new se memory leta hai, tab problem shuru hoti hai. Agar tum copy constructor aur copy assignment khud nahi likhte, to compiler ek shallow copy banata hai — matlab sirf pointer ka address copy hota hai, andar ki memory nahi. Ab do objects ek hi heap buffer ko point karte hain. Jab dono destruct hote hain, dono delete[] chalate hain same pointer par → double free, program crash. Isi liye deep copy chahiye: nayi memory allocate karo aur content copy karo.
Rule of Three kehta hai: agar tumne in teeno me se ek bhi khud likha — destructor, copy constructor, ya copy assignment — to teeno likhne padenge. Kyunki teeno ek hi cheez describe karte hain: resource kaise paida, kaise copy, aur kaise destroy hota hai. Ek bhi chhoot gaya to bug pakka.
Sabse zyada galti operator= me hoti hai. Yaad rakho do baatein: (1) self-assignment guard — if (this == &other) return *this;, warna a = a me pehle apni memory free kar doge aur phir usi se copy karoge (garbage). (2) purani memory free karo — assignment me object pehle se ek buffer rakhta hai, use delete[] karo warna memory leak. Copy constructor me ye free wala step nahi hota kyunki object abhi-abhi bana hai, uske paas purana buffer hota hi nahi.
Smart tarika: copy-and-swap idiom — parameter ko value se lo (copy automatically ban jayegi), phir std::swap se andar ka data badal do. Ye self-assignment aur exception dono ke liye safe hai. Aur modern C++ me best — Rule of Zero: std::string, std::vector, unique_ptr use karo, to tumhe in teeno me se kuch likhna hi nahi padega.