Sobes.tech
Middle+

What should be remembered when throwing exceptions in a constructor?

sobes.tech AI

Answer from AI

  1. Resource leaks: If a constructor throws an exception after some resources have been allocated (memory, file descriptors, etc.), but before the object has finished initializing, these resources may not be released if the object's destructor is not called.
  2. Partially constructed objects: An object whose constructor has thrown an exception is considered incomplete. The destructor for such an object is not called automatically.
  3. RAII (Resource Acquisition Is Initialization): Using RAII wrappers (e.g., std::unique_ptr, std::vector, std::lock_guard) to manage resources helps automatically release them when an exception is thrown, as the destructors of RAII objects used in the constructor are called during stack unwinding.
  4. Order of member initialization: Class members are initialized in the order of their declaration, not in the order of their initialization in the constructor's initializer list. If member initialization throws an exception, members initialized before may require cleanup.
  5. noexcept: A class constructor with the noexcept specifier should not throw exceptions. If an exception is thrown in a noexcept constructor, the program will terminate (default behavior).
  6. new operator: The new operator itself can throw a std::bad_alloc exception if there is insufficient memory.

Example of RAII usage:

#include <memory>
#include <stdexcept>

class Resource {
public:
    Resource() { /* Resource allocation */ }
    ~Resource() { /* Resource release */ }
};

class MyClass {
    std::unique_ptr<Resource> res1;
    std::unique_ptr<Resource> res2;
public:
    MyClass() : res1(std::make_unique<Resource>()) {
        // If an exception is thrown here, res1 will be released
        if (/* condition for exception */ true) {
            throw std::runtime_error("Error during construction");
        }
        res2 = std::make_unique<Resource>(); // This line will not be reached
    }
};

int main() {
    try {
        MyClass obj;
    } catch (const std::exception& e) {
        // res1 will be automatically released thanks to unique_ptr
        // res2 was never created
    }
    return 0;
}