5.2.29 · D2 · HinglishC++ Programming

Visual walkthroughException safety — basic, strong, no-throw guarantees

2,456 words11 min read↑ Read in English

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.

Figure — Exception safety — basic, strong, no-throw guarantees

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.

Figure — Exception safety — basic, strong, no-throw guarantees

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.

Figure — Exception safety — basic, strong, no-throw guarantees

Step 3 — Commit ek non-throwing move hona chahiye

    swap(*this, tmp);   // (C) commit — noexcept, cannot throw
}                       // (D) ~tmp runs, freeing the OLD array
friend 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).

Figure — Exception safety — basic, strong, no-throw guarantees

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 ✅
Figure — Exception safety — basic, strong, no-throw guarantees

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).

Figure — Exception safety — basic, strong, no-throw guarantees

Ek-picture summary

Figure — Exception safety — basic, strong, no-throw guarantees

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.

Line (3) leak karta hai — fresh new[] block ek local raw pointer mein baitha hai jiske paas use free karne ke liye koi destructor nahi
correct
Swap sirf data pointers aur n sizes exchange karta hai — trivial assignments, koi allocation nahi, toh kuch bhi throw nahi kar sakta
correct
Kyunki visible object sirf swap par change hota hai, aur swap noexcept hai — usse pehle, throws sirf tmp ko hit karti hain
correct
Kyunki tmp ek independent snapshot hai jo growth se pehle liya gaya hai, src se reads kabhi bhi clobbered data nahi dekhte
correct