NOW LET US – AI RAG SaaS Studio TP.HCM
NOW LET US
Digital Product Studio
Back to news
DEV-TOOLS...4 min read

What happens when a destructor throws

Share
NOW LET US Article – What happens when a destructor throws

An in-depth look at C++ exception handling mechanisms, explaining why throwing from a destructor is dangerous and how the language handles such scenarios.

Recently I wrote about the importance of finding joy in our jobs on The Dev Ladder. Mastery and deep understanding are key elements in finding that joy, especially now that generating code is cheap and increasingly done better by AI than by us.

Then a memory surfaced. I frequently ask during interviews — as part of a code review exercise — what happens when a destructor throws. Way too many candidates, even those interviewing for senior positions, cannot answer the question. Most say it’s bad practice, but cannot explain why. Some say the program might terminate. Getting an elaborate answer is rare.

I’m not saying it’s a dealbreaker, but it definitely doesn’t help.

Let’s see what actually happens.

The role of a destructor

A destructor is the key to implementing the RAII idiom. RAII matters because after you acquire a resource, things might go south. A function might need to return early, or it might throw. Making sure resources are released is cumbersome, and the cleanest way to achieve it is to wrap both acquisition and release in an object that handles this automatically.

But what if the release itself is not successful?

Destructors have no return value, so error reporting is limited. Typical options include logging, storing error state, or (discouraged) throwing.

Why did I mark throwing an exception discouraged?

What happens when an exception is thrown

When an exception is thrown, runtime stack unwinding starts.

First, automatic objects in the current scope are destroyed in reverse order, with their destructors executed.

If another exception is thrown during unwinding, std::terminate is called.

If a matching exception handler is found, execution continues there.

What if a destructor throws with no other active exception?

Let’s start with a simple example where no exception handling is ongoing:

#include <iostream>
struct A {
~A() {
std::cout << "Destructor\n";
throw std::runtime_error("boom");
}
};
int main() {
try {
A a;
} catch (const std::exception& e) {
std::cout << "Caught: " << e.what() << "\n";
}
}

Let’s go step by step:

  • we enter the try block
  • A a is constructed
  • a’s scope ends and ~A() is called
  • A::~A() throws

And then… we have to stop for a second and recall an important rule: Since C++11, destructors are implicitly noexcept(true) unless declared otherwise or a base or member destructor can throw.

As an exception would leave our noexcept destructor, the noexcept guarantee is violated, so std::terminate is called. The catch block is never reached.

What if we want the destructor to be allowed to throw? Let’s update the example and mark the destructor throwable with noexcept(false):

#include <iostream>
struct A {
~A() noexcept(false) {
std::cout << "Destructor\n";
throw std::runtime_error("boom");
}
};
int main() {
try {
A a;
} catch (const std::exception& e) {
std::cout << "Caught: " << e.what() << "\n";
}
}

In this case, the exception propagates normally and the catch block intercepts it. So a destructor can throw as long as it’s explicitly marked noexcept(false). I said can, not should. And there’s a critical caveat.

What if a destructor throws while another exception is active?

What if a destructor throws during stack unwinding? Let’s update our example slightly:

#include <iostream>
struct A {
~A() noexcept(false) {
std::cout << "Destructor throwing\n";
throw std::runtime_error("boom");
}
};
int main() {
try {
A a;
throw std::runtime_error("original");
} catch (...) {
std::cout << "caught\n";
}
}

Let’s go through what happens step by step:

  • we enter the try block
  • throw std::runtime_error("original") is thrown and stack unwinding starts
  • as part of the unwinding, local objects are destroyed, so A::~A() is called
  • A::~A() throws a second exception while the original is still active — std::terminate() is called

This termination is mandated by the C++ standard and no catch block is reached. The rule exists because otherwise the runtime would need to track multiple simultaneous propagations, which would be complex and almost certainly ambiguous.

Conclusion

When a destructor throws, the outcome depends on context. If no other exception is active and the destructor is explicitly marked noexcept(false), the exception propagates normally and can be caught. But this is the exception — in both senses of the word. Since C++11, destructors are implicitly noexcept(true), so a throwing destructor will call std::terminate by default, bypassing any catch block entirely.

The real danger is the second scenario: a destructor throwing while stack unwinding is already in progress. Even with noexcept(false), this always calls std::terminate. You cannot let an exception escape a destructor during unwinding — the standard simply does not allow it.

This is why the conventional wisdom holds: destructors should not throw. If resource release fails, the alternatives — logging the error, setting an error flag, or storing the failure state for later inspection — are far safer than propagating exceptions out of a destructor.

© 2026 Now Let Us. All rights reserved.

Source: Hacker News

Advertisement
Ad slot ready: 5887729102

More in this category

NOW LET US Related – GLM 5.2 Is Out

dev-tools

GLM 5.2 Is Out

Zhipu AI has officially released GLM-5.2, its most powerful open-source model to date, featuring a 1M context window and advanced long-horizon task capabilities. The release underscores Zhipu's commitment to open-source AI and global scientific collaboration amid rising technological restrictions.

NOW LET US Related – Treating pancreatic tumours may have revealed cancer's master switch

dev-tools

Treating pancreatic tumours may have revealed cancer's master switch

A promising new drug called daraxonrasib has shown breakthrough results in treating pancreatic cancer, doubling median survival times. This achievement could pave the way for an entirely new class of cancer treatments.

NOW LET US Related – Leaving Mozilla

dev-tools

Leaving Mozilla

A poignant and candid reflection from a 15-year Mozilla veteran upon their departure. The author highlights the leadership's missteps in trying to emulate tech giants and urges Mozilla to return to its core values: community and uniqueness.

NOW LET US Related – Shepherd's Dog: A Game by the Most Dangerous AI Model

dev-tools

Shepherd's Dog: A Game by the Most Dangerous AI Model

A developer tested Anthropic's latest, supposedly 'too dangerous' AI model by asking it to build a long-held game idea in a single shot. The model succeeded, generating a complete 2,319-line game after a 45-minute reasoning session.

NOW LET US Related – Open source AI must win

dev-tools

Open source AI must win

If artificial intelligence becomes a utility rented only from a few closed institutions, humanity loses its operational freedom. Open-source AI is a vital infrastructure for the future of our digital society.

NOW LET US Related – Statement on US government directive to suspend access to Fable 5 and Mythos 5

dev-tools

Statement on US government directive to suspend access to Fable 5 and Mythos 5

The US government has issued an export control directive forcing Anthropic to suspend all access to its Fable 5 and Mythos 5 models due to national security concerns, a move the AI safety startup strongly disputes.

EXPLORE TOPICS

Discover All Categories

Deep dive into the specific technology sectors that matter most to you.