Visual walkthrough — Exception safety — basic, strong, no-throw guarantees
5.2.29 · D2· Coding › C++ Programming › Exception safety — basic, strong, no-throw guarantees
Yeh parent topic ka picture-first companion hai. Definitions ke liye woh padho; machinery dekhne ke liye yeh padho.
Step 0 — Vocabulary, use karne se pehle draw karo
Kisi bhi derivation se pehle, teen plain-English ideas, har ek neeche ek picture ke saath pin ki gayi hai.

Hum poore time ek concrete operation follow karenge: ek class Buffer jo ek heap array own karti hai, aur hum usme kuch numbers append karte hain. Appending ke liye bigger array chahiye, toh allocate aur copy karna zaroori hai — classic jagah jahan exception chupi rehti hai.
Step 1 — Naive in-place version, aur usme tripwire
void append_naive(const int* src, std::size_t k) {
int* bigger = new int[n + k]; // (1) allocate — may throw bad_alloc
std::copy(data, data + n, bigger); // (2) copy old — a copy may throw
std::copy(src, src + k, bigger+n); // (3) copy new — may throw
delete[] data; // (4) free old
data = bigger; n += k; // (5) commit pointers
}Yahan data woh label hai jo hamare current array ki taraf point karta hai, n hai kitne numbers hain, src incoming numbers hain aur k hai kitne hain.

Step 2 — Risky kaam ek stand-in copy par move karo
void append_strong(const int* src, std::size_t k) {
Buffer tmp(*this); // (A) copy — may throw, but only tmp is at stake
tmp.grow_in_place(src, k); // (B) risky work — happens on tmp
// ... commit comes next ...
}tmp haara stand-in hai — ek doosri jar jo *this jaisi hi contents se bhari hai.

Step 3 — Commit ek non-throwing move hona chahiye
swap(*this, tmp); // (C) commit — noexcept, cannot throw
} // (D) ~tmp runs, freeing the OLD arrayfriend void swap(Buffer& a, Buffer& b) noexcept {
std::swap(a.data, b.data); // trade the two pointers
std::swap(a.n, b.n); // trade the two sizes
}Figure mein dekho do labels cross over karte hain. Is crossover mein koi pal nahi jahan ek throw hume half-committed chhod sake — exchange trivial assignments ka sequence hai, aur agar aap program ko unke beech freeze karo bhi toh object abhi bhi fully valid hai (worst case yeh nanosecond bhar ke liye naye array ki taraf point kar raha hai purane n ke saath, lekin koi exception wahan freeze karne ke liye nahi aa sakti).

Step 4 — Poori timeline: har failure point covered
| Throw at | *this (visible) |
tmp (stand-in) |
Verdict |
|---|---|---|---|
| (A) copy | untouched (old) | half-built → auto-freed by ~tmp |
no effect, no leak ✅ |
| (B) grow | untouched (old) | partly grown → auto-freed by ~tmp |
no effect, no leak ✅ |
| (C) swap | cannot throw (noexcept) |
— | impossible ✅ |
| (D) destroy | new state, committed | freeing old array (dtor never throws) | already succeeded ✅ |

Step 5 — Degenerate cases (inhe kabhi skip mat karo)
Case k = 0 (kuch append nahi). Copy (A) mein abhi bhi run hoti hai, grow_in_place zero naye numbers copy karta hai, swap identical-valued buffer commit karta hai. Koi throw source remove nahi, koi throw source add nahi — abhi bhi strong. Cost hai ek bekar copy; correctness untouched hai.
Case self-append (src data ke andar point karta hai). Kyunki (A) pehle copy karta hai, tmp purane numbers ka ek independent snapshot hold karta hai kisi bhi growing hone se pehle. Toh src se read karna jabki tmp mein write karo kabhi bhi clobbered value nahi padhta — copy-first design self-reference ko apne aap safe banata hai, usi tarah jaise copy-and-swap assignment self-assignment-safe hoti hai (dekho Copy-and-swap idiom).
Case jab type ka move throw kare. Agar hum growth ke dauraan elements ko copy karne ki jagah move karne ki koshish karte aur woh move throw kar sakta, toh throw mid-growth source ko gut kar deta — aur std::vector jaise container ke liye iska matlab hai strong guarantee kho jaati hai. Isliye library std::move_if_noexcept use karti hai: yeh copy karta hai jab tak move noexcept mark na ho (dekho Move semantics and noexcept aur std::vector reallocation strategy).

Ek-picture summary

Poora idea ek frame mein: ek throw-zone (copy + grow, stand-in jar par drawn) aur phir ek safe-zone commit (label swap). Har tripwire swap ke baaye taraf hai; swap khud ek aisi wall hai jise koi tripwire cross nahi kar sakti. Isliye visible object ya toh fully old hai ya fully new — beech mein kabhi nahi.
Recall Feynman retelling — walkthrough bina jargon ke explain karo
Aapko ek jar renovate karni hai apni shelf wali jar ko kharaab kiye bina.
Clumsy tarika (Step 1): aap shelf-jar ko table par ughal dete ho aur naya saamaan dalna shuru karte ho. Agar aapka phone bajta hai (ek throw) pouring ke beech mein, table par ganda mess hai jise koi saaf nahi karta (ek leak) aur ek shelf-jar jo half-empty hai (broken).
Safe tarika: pehle ek second jar banao jo shelf wali ki perfect copy ho (Step 2). Saari messy pouring us doosri jar mein karo. Phone bajta hai? Sirf doosri jar messy hai — aur kyunki yeh ek "self-cleaning" jar hai, jaise hi aap ise drop karte ho woh khud saaf ho jaati hai (RAII). Shelf-jar kabhi touch hi nahi ki gayi.
Jab doosri jar finally perfect ho, toh aap ise wapas pour nahi karte — pouring se girachna ho sakta hai. Bajaye iske aap bas do labels swap karte ho (Step 3): shelf label ab nayi jar ko naam deta hai, aur purani jar ko spare label milta hai aur phek di jaati hai. Labels swapping se kabhi kuch nahi girta, toh yeh aakhri step ek aisi wall hai jise koi interruption cross nahi kar sakti.
Phone ke har possible ring ko check karo (Step 4): swap se pehle sirf spare jar hurt hoti hai; swap interrupt nahi ho sakti; swap ke baad aap already done ho. Aur awkward cases (Step 5) — kuch nahi dalna, jar mein se usi jar mein daalna, ya aisi "pour" use karna jo khud gir sakti hai — sab theek behave karte hain, kyunki humne pehle copy kiya aur move tab hi trust kiya jab usne promise kiya ki kabhi nahi girega. Wahi hai copy-and-swap, aur wahi hai strong guarantee.