Should destructors throw?
No—if a destructor throws during unwind, std::terminate is called.
Master C++ exception handling: try-catch blocks, exception specifications, RAII pattern, and exception safety guarantees. Learn with practical examples and best practices.
Protected Code
Exception Handler
Raise Exception
No Exceptions
Exceptions separate error paths from normal logic. Learn how to throw and catch, use standard exception types, and pair exceptions with RAII for leak-free cleanup.
Exception safety is asked in senior interviews. Companies expect you to avoid bare new and to understand stack unwinding.
Exception handling is a mechanism that allows programs to deal with unexpected situations (exceptions) during execution. It provides a structured way to separate error-handling code from normal program logic.
The fundamental exception handling mechanism in C++ consists of three keywords: try, catch, and throw.
try {
// Code that might throw exceptions
if (error_condition) {
throw exception_object;
}
}
catch (exception_type& e) {
// Handle exception
}
try {
throw 42;
} catch (int e) {
cout << e;
}catch (const exception& e) {
cout << e.what();
}throw string("Invalid input");catch (int e) { }
catch (double e) { }
catch (...) { }try {
try { throw 1; }
catch (int e) { throw; }
} catch (...) {}catch (exception& e) {
cerr << e.what();
throw;
}C++ provides a hierarchy of standard exception classes in the <stdexcept> header. These classes represent common error conditions.
logic_errorinvalid_argumentdomain_errorlength_errorout_of_rangefuture_error (C++11)runtime_errorrange_erroroverflow_errorunderflow_errorsystem_error (C++11)throw invalid_argument("bad age");vector v={1};
cout << v.at(5); if (!file) throw runtime_error("open failed");throw logic_error("precondition failed");int* p = new int[1e9]; // may throwclass MyError : public exception {
const char* what() const noexcept override { return "My"; }
};Use logic_error for preventable programming errors. Use runtime_error for errors that could occur despite correct program logic. Always prefer standard exceptions over custom ones when they fit.
Exception specifications declare what exceptions a function might throw. noexcept (C++11) indicates a function won't throw exceptions.
// C++98 style (deprecated)
void func() throw(int, std::runtime_error);
void func() throw(); // No exceptions
// Modern C++ style
void func() noexcept; // Won't throw
void func() noexcept(true);
void func() noexcept(false);
void swap(int& a,int& b) noexcept {
int t=a; a=b; b=t;
}void mayFail() noexcept(false) { throw 1; }cout << noexcept(func());// void f() throw(); // old stylevector(vector&&) noexcept;template
void f() noexcept(noexcept(T())); std::terminate() on violationunexpected() then terminate()RAII (Resource Acquisition Is Initialization) is a fundamental C++ idiom that ties resource management to object lifetime, ensuring proper cleanup even when exceptions occur.
class Resource {
Handle* handle;
public:
Resource() : handle(acquire_resource()) {
// Resource acquired in constructor
}
~Resource() {
release_resource(handle); // Resource released in destructor
}
// Disable copying (or implement properly)
Resource(const Resource&) = delete;
Resource& operator=(const Resource&) = delete;
};
mutex m;
lock_guard lock(m);
// unlocks even if throw auto p = make_unique(5); ifstream in("data.txt");
// closes on scope exitvector backup = v;
try { v.push_back(x); }
catch(...) { v=backup; throw; } class User {
string name;
vector scores;
}; ~File() { if (fp) fclose(fp); }std::terminate()noexceptAdvanced exception handling patterns including nested exceptions, exception propagation, and handling strategies for large systems.
exception_ptr ep = current_exception();
rethrow_exception(ep);throw nested_exception();set_terminate([]{ cerr << "terminate"; abort(); });void g() noexcept { /* no throw */ }try { init(); } catch(...) { cleanup(); throw; }Foo() try : x(0) {} catch(...) { throw; }noexceptnoexceptstd::terminate()std::terminate()catch (const invalid_argument& e) {}catch (const exception& e) { log(e.what()); }catch (...) { /* log and rethrow or handle */ }unique_ptr p = make_unique(); catch (...) { } // hides bugsif (bad) throw 1; // prefer exception typesThe following table compares different exception safety guarantees with their characteristics and implementation requirements:
| Safety Level | Guarantee | Characteristics | Implementation Requirements |
|---|---|---|---|
| No-throw Guarantee | Operation never throws exceptions | Strongest guarantee, marked with noexcept |
All operations must be non-throwing |
| Strong Guarantee | Commit-or-rollback semantics | Operation succeeds completely or has no effect | Copy-and-swap idiom, operations on temporaries |
| Basic Guarantee | No resource leaks, valid state | Objects remain valid but unspecified state | RAII for resources, destructor cleanup |
| No Guarantee | No promises | Resource leaks possible, invalid state possible | Avoid! Never acceptable in production code |
No—if a destructor throws during unwind, std::terminate is called.
Basic: no leaks after exception; strong: state unchanged if throw; noexcept: never throws.
Catch const std::exception& to avoid slicing polymorphic exception objects.