When a constructor throws an exception during execution, C++ automatically cleans up objects that were successfully constructed before the exception occurred. Specifically, only the destructors of members and base classes that completed construction will be invoked. This is to prevent resource leaks.
For illustration, consider the following example:
cpp#include <iostream> #include <stdexcept> class A { public: A() { std::cout << "A constructed" << std::endl; } ~A() { std::cout << "A destroyed" << std::endl; } }; class B { public: B() { std::cout << "B constructed" << std::endl; } ~B() { std::cout << "B destroyed" << std::endl; } }; class C { public: C() { std::cout << "C constructed" << std::endl; throw std::runtime_error("exception thrown in C"); } ~C() { std::cout << "C destroyed" << std::endl; } }; class Test { A a; B b; C c; public: Test() { std::cout << "Test constructed" << std::endl; } ~Test() { std::cout << "Test destroyed" << std::endl; } }; int main() { try { Test t; } catch (const std::exception &e) { std::cout << "Caught exception: " << e.what() << std::endl; } return 0; }
In this example, the Test class contains three members: A a, B b, and C c. When attempting to construct an object of the Test class:
- First, construct member
A a, outputting 'A constructed' upon success. - Next, construct member
B b, outputting 'B constructed' upon success. - Then, attempt to construct member
C c, throwing an exception during construction and outputting 'C constructed' and 'exception thrown in C'.
Because an exception occurs during the construction of C c, its destructor is not called since it never completed construction. However, for A a and B b that have successfully constructed, their destructors will be called in sequence, outputting 'B destroyed' and 'A destroyed'.
This mechanism ensures that resources allocated during construction are properly reclaimed, preventing resource leaks.