5.2.29 · D5 · HinglishC++ Programming

Question bankException safety — basic, strong, no-throw guarantees

1,833 words8 min read↑ Read in English

5.2.29 · D5 · Coding › C++ Programming › Exception safety — basic, strong, no-throw guarantees


True ya false — justify karo

Har statement ya to true hai ya false. Bolo kaun sa, aur kyun — reason hi point hai.

Ek noexcept function automatically strong guarantee deta hai.
True — agar woh kabhi throw hi nahi kar sakta, to "throw hone par operation ka koi effect nahi" vacuously satisfied hai kyunki throw ho hi nahi sakta. No-throw, ladder mein strong se upar hota hai.
Strong guarantee ka matlab hai ki operation fail nahi ho sakta.
False — woh bilkul fail ho sakta hai (throw kar sakta hai); promise sirf yeh hai ki failure par state roll back ho jaye exactly wahi jo pehle thi. "Fail nahi ho sakta" wali baat alag no-throw guarantee hai.
Basic guarantee promise karta hai ki throw ke baad variable values unchanged rahenge.
False — basic sirf yeh promise karta hai ki koi leak nahi aur ek valid, destructible lekin unspecified state rahegi. Values change ho sakti hain; sirf invariants guaranteed hain ki still hold karenge.
Agar har resource RAII mein wrap hai, to automatically strong guarantee milti hai.
False — RAII tumhe basic guarantee deta hai (no leaks, cleanup on unwind). Strong ke liye ek extra commit-or-rollback design chahiye, jaise RAII ke upar copy-and-swap.
Ek function jo kisi bhi observable state ko modify karne se pehle throw karta hai, woh strong guarantee deta hai.
True — agar throw kisi bhi visible change se pehle hota hai, to caller exactly pre-call state dekhta hai, jo precisely "no effect" hai. Order-of-operations tumhe free mein strong de sakta hai.
swap ka noexcept hona hi copy-and-swap ko atomically commit karne deta hai.
True — risky copy work ek temporary par hoti hai; final swap hi woh ek step hai jo real object ko touch karta hai, aur kyunki woh throw nahi kar sakta, commit ya to poora hota hai ya mid-way pahuncha hi nahi.
Ek function ko noexcept mark karna use bina kisi downside ke faster aur safer banata hai.
False — agar ek noexcept function kabhi throw kare, to runtime std::terminate call karta hai bina kisi stack unwinding ke. No-throw ke baare mein jhooth bolna fatal hai, free nahi.
"No guarantee" level phir bhi guarantee karta hai ki koi undefined behaviour nahi hoga.
False — "no guarantee" sabse worst case hai: leaks, broken invariants, double-frees, aur undefined behaviour sab possible hain. Yahi woh state hai jiske against hum design karte hain.
Ek destructor jo throw karta hai, theek hai jab tak tum andar exception ko catch kar lo.
Roughly true sirf tab agar woh throw ko kabhi escape na hone de — ek destructor jo stack unwinding ke dauran exception ko bahar jane deta hai woh std::terminate cause karta hai. Practically, destructors ko effectively noexcept treat karo.
std::vector::push_back hamesha strong guarantee deta hai.
True push_back call ke liye akele (reallocation par woh roll back karta hai), lekin sirf isliye kyunki reallocation copy karta hai ya noexcept-moves karta hai. Doosre side-effecting statements ke saath compose karne par, enclosing function basic par aa sakta hai.

Error dhundo

Har snippet ek guarantee claim karta hai. Bolo kya galat hai, ya confirm karo ki sahi hai aur kyun.

void g(){ auto p = new Widget(); p->risky(); delete p; } — basic claim karta hai.
Galat — agar p->risky() throw kare, to delete p skip ho jata hai aur Widget leak ho jata hai. Ek throwing call ke aas-paas raw new/delete no guarantee deta hai; unique_ptr se fix karo.
Buffer& operator=(const Buffer& rhs){ delete[] data; data = new int[rhs.n]; ... } — strong claim karta hai.
Do counts par galat: new int[...] delete[] data ke baad throw kar sakta hai, data ko dangling chhod ke (broken invariant), aur yeh self-assignment safe bhi nahi hai. Pass-by-value copy-and-swap dono ko fix karta hai.
Copy-and-swap jahan swap ek plain member-by-member std::swap hai int aur pointers par — strong claim karta hai.
Correct — built-in scalars aur raw pointers ko swap karna noexcept hai, isliye commit throw nahi kar sakta aur throwing copy work temporary par hi raha. Yahi intended shape hai.
void h(std::vector<int>& v,int x){ v.push_back(x); log(x); } jahan log throw kar sakta hai — strong claim karta hai.
Galat — push_back succeed hone ke baad, log mein throw hone par element already push ho chuka hota hai. Visible side effect persist karta hai, isliye yeh sirf basic hai. Strong ke liye ek copy par push karo aur last mein swap karo.
Ek move constructor Buffer(Buffer&& o) noexcept { data=o.data; o.data=nullptr; } noexcept mark kiya gaya hai lekin andar ek throwing logger call karta hai.
Galat — body logger ke through throw kar sakta hai, isliye noexcept ek jhooth hai; yahan ek real throw std::terminate call karta hai. Ya to throwing call hataao ya noexcept drop karo.
friend void swap(Buffer&a,Buffer&b){ Buffer t=a; a=b; b=t; } copy-and-swap ke andar use hota hai.
Galat — yeh "swap" copies karta hai (copy constructor/assignment call karta hai, jo throw kar sakta hai), poore point ko defeat karta hai. Real swap ko member-wise handles exchange karna chahiye aur noexcept hona chahiye.

Why questions

Ek ya do sentences mein real reasoning ke saath "why" ka jawab do.

RAII automatically basic guarantee kyun deta hai?
Kyunki har resource ka release ek stack object ke destructor se tied hota hai, aur stack unwinding kisi bhi throw par un destructors ko run karta hai — isliye cleanup kabhi "skip" nahi ho sakta jaise ek missed delete ho jata.
Copy-and-swap mein swap step hi woh hona chahiye jo throw nahi kar sake, copy kyun nahi?
Copy throw kar sakti hai kyunki woh sirf temporary ko touch karti hai, original intact rehta hai; swap woh jagah hai jahan observable object badalta hai, isliye wahi ek step hai jo atomic aur non-throwing hona chahiye.
std::vector reallocation par kabhi kabhi move karne ki jagah copy kyun karta hai?
Ek move jo beech mein throw kare woh purane buffer ko adha gutted chhod deta, strong guarantee destroy kar deta. Agar element ka move constructor noexcept nahi hai, to vector copy karta hai (source untouched) taaki woh roll back kar sake — yahi std::move_if_noexcept rule hai.
Ek composed operation sirf apne weakest rollback jaisi hi strong kyun hoti hai?
Kyunki guarantees poore function ki after-throw state describe karti hain; ek baad wala throwing step jo pehle se koi visible change commit kar chuka ho, matlab poori operation "no effect" claim nahi kar sakti, chahe har piece individually strong thi.
Ek noexcept violation std::terminate call karta hai propagate karne ki jagah — kyun?
Compiler ne noexcept promise use karke callers ke liye unwinding machinery generate karna skip kar diya; exception ko escape hone dena us assumption ko break kar deta, isliye standard immediate terminate mandate karta hai.
Copy-and-swap self-assignment safe "for free" kyun hai?
Pass-by-value form mein argument body run hone se pehle hi ek independent copy hai, isliye apne aap ke saath swap karna simply object ko uski apni copy se swap karna hai — koi aliasing hazard nahi, koi special check zaroorat nahi.
Hum kehte hain noexcept move constructors par ek performance feature hai, sirf safety nahi — kyun?
Kyunki yeh std::vector jaise containers ko reallocation ke dauran elements ko move karne deta hai copying ki jagah; noexcept promise ke bina woh slower copies par fall back karte hain strong safety preserve karne ke liye.

Edge cases

Woh boundary aur degenerate cases jo topic quietly invite karta hai.

Ek function jo bilkul kuch nahi karta (empty body) kya guarantee deta hai?
No-throw guarantee — woh throw nahi kar sakta aur koi observable effect nahi hai, isliye trivially ladder ke top par hota hai.
Copy-and-swap append mein zero elements append karna (k == 0) — kya guarantee hold hoti hai?
Phir bhi strong: ek no-op path bhi copy phir swap karta hai (ya kisi visible change se pehle short-circuit ho jata hai), isliye original either way untouched hai aur roll back karne ke liye kuch nahi hai.
Agar Buffer tmp(*this) ek huge allocation ke saath throw kare, to *this kis state mein hai?
Exactly apni pre-call state mein — throw kisi bhi swap se pehle hua, isliye kuch bhi observable change nahi hua. Yahi strong guarantee design ke hisaab se kaam karti hai.
Ek type jiska move constructor not marked noexcept hai lekin genuinely kabhi throw nahi karta — kya vector optimal hai?
Nahi — vector dekh nahi sakta ki yeh safe hai, isliye conservatively copies karta hai. Optimization sirf visible noexcept promise se unlock hoti hai, actual runtime behaviour se nahi.
Self-assignment x = x naive "delete then copy" assignment ke through — kya break hota hai?
delete[] data usi buffer ko free kar deta hai jisse tum copy karne wale ho, isliye tum freed memory copy karte ho: undefined behaviour. Copy-and-swap isse avoid karta hai kyunki copy kisi bhi deletion se pehle banti hai.
new se akele allocation kya guarantee deti hai agar throw hone se pehle tum pointer store nahi karte?
Us step ke liye strong guarantee — new jo throw kare (std::bad_alloc) kabhi pointer return nahi karta, isliye kuch acquire nahi hua aur kuch leak nahi hota; state unchanged hai.
Agar ek chain mein har operation noexcept hai sivaay ek final logging call ke, to overall guarantee kya hai?
Sirf basic (ya weaker) — poori chain throwing step se cap hoti hai, jo pehle ke visible changes commit ho jaane ke baad run ho sakti hai, isliye "no effect" ab promise nahi kiya ja sakta.

Recall Ise lock karne ke liye ek-line summary

Ladder question ::: Basic = valid + no leak (RAII); Strong = commit-or-rollback (copy-and-swap on a temporary); No-throw = throw nahi kar sakta (noexcept swaps/moves/dtors). Ek composed operation sirf apne weakest rollback jaisi hi strong hoti hai.