Exception Handling
এক্সেপশন হ্যান্ডলিং
1. try / catch / throw
#include <iostream>
#include <stdexcept>
int divide(int a, int b) {
if (b == 0) throw std::invalid_argument{"divide by zero"};
return a / b;
}
int main() {
try {
std::cout << divide(10, 0) << "\n";
} catch (const std::invalid_argument& e) {
std::cerr << "Caught: " << e.what() << "\n";
} catch (const std::exception& e) {
std::cerr << "Other: " << e.what() << "\n";
}
}
2. The Standard Exception Hierarchy
std::exception
std::logic_error:invalid_argument,domain_error,length_error,out_of_rangestd::runtime_error:range_error,overflow_error,underflow_errorstd::bad_alloc,std::bad_cast
3. Always Catch by const Reference
const std::exception&
By value → slicing. By non-const ref → can't bind to temp. const T& is correct.
4. noexcept
Marks a function as guaranteed not to throw. The compiler can optimize, and STL containers use it for move-on-reallocate decisions.
int add(int a, int b) noexcept { return a + b; }
5. Stack Unwinding & RAII
When an exception propagates up, every local object whose scope is exited has its destructor run. That's why RAII makes exception-safe code automatic — your resources clean themselves up.
6. Exception Safety Levels
- No-throw guarantee: function never throws.
- Strong guarantee: if it throws, state is unchanged (atomic-like).
- Basic guarantee: if it throws, no leaks but state may be different.
- No guarantee: avoid this.
7. When NOT to Use Exceptions
- For normal control flow (e.g. parsing every number in a file)
- In real-time systems with strict latency budgets
- When the project has a "no exceptions" rule (game engines, embedded)
For "expected failure" use std::optional, std::expected (C++23), or error codes.
8. Practice Problems
- Write a function that throws on negative input.
✨ Show Answer
int sqrt_int(int n) { if (n < 0) throw std::domain_error{"negative"}; return (int)std::sqrt(n); } - Catch all exceptions.
✨ Show Answer
catch (...) { /* fallback */ } - Why catch by const reference?
✨ Show Answer
By value slices polymorphic exceptions. By const ref preserves the dynamic type and avoids copies.
- Define your own exception class.
✨ Show Answer
class ParseError : public std::runtime_error { public: using std::runtime_error::runtime_error; }; throw ParseError{"bad token"}; - What happens if no catch matches?
✨ Show Answer
std::terminateis called → process aborts. Always have a top-level catch in main. - Mark a destructor as noexcept(false). Why is this dangerous?
✨ Show Answer
If a destructor throws during stack unwinding (caused by another exception),
std::terminateis called. Destructors should never throw. - Re-throw an exception.
✨ Show Answer
catch (...) { log("oops"); throw; } - Why does RAII make exception safety easier?
✨ Show Answer
Cleanup is automatic via destructors during stack unwinding. You don't need
try/finally— the destructor handles it. - Catch order — base or derived first?
✨ Show Answer
Derived first. Catches are tried top-to-bottom; if you catch
std::exception&first, derived classes never reach their handlers. - When is exception use a code smell?
✨ Show Answer
For control flow over routine cases (e.g. "is this number even? throw if odd"). Exceptions are for exceptional cases.
- What is
std::expected?✨ Show Answer
C++23 type holding either a value or an error. A type-safe alternative to throwing for expected failures.
- Catch invalid_argument from std::stoi.
✨ Show Answer
try { int n = std::stoi("abc"); } catch (const std::invalid_argument& e) { /* ... */ }
Summary
Use exceptions for exceptional situations. Catch by const T&. Mark non-throwing
functions noexcept. RAII + stack unwinding makes cleanup automatic. For routine errors,
prefer std::optional or std::expected.