1

I've found what looks like contradictory answers regarding exceptions being thrown inside constructors in C++. One of the answers in this link says that if an exception is thrown inside a constructor, it is assumed that construction is not complete, and therefore the the destructor is not called. But this link discusses the RAII concept, using the example of a mutex created in a constructor and cleaned up in a destructor. It says that if a mutex is created in the constructor and then the constructor calls a function that throws an excpetion and no exception handler is defined, then the destructor will still be called and the mutex will be cleaned up. What am I missing?

ROBERT RICHARDSON
  • 2,077
  • 4
  • 24
  • 57
  • 3
    Please post some code that illustrates exactly what you are asking about. Those questions you linked to cover a lot of ground. –  May 06 '19 at 20:01
  • 1
    Edited a bit: _"If an exception is thrown from a constructor [...] of an object (regardless of the object's storage duration), destructors are called for all fully-constructed non-static non-variant [] members and base classes, in reverse order of completion of their constructors. ..."_ full source: https://en.cppreference.com/w/cpp/language/throw – Richard Critten May 06 '19 at 20:04

2 Answers2

3

The destructor of the object being constructed is not executed, but all its members that were constructed are destructed; for example:

struct A {
   A(int x) { ... }
   ~A() { ... }
};

struct B {
   A a1, a2, a3;
   B() : a1(1), a2(2), a3(3) { ... }
   ~B() { ... }
};

if when building a B instance the construction of a1 goes well, then the construction of a2 goes well but the construction of a3 throws an exception then what happens is that a2 will be destroyed (calling ~A), then a1 will be destroyed but ~B will NOT be called because the constructor didn't complete (the body didn't even start).

Even if the exception is thrown inside the ... body of B() then all of the A subobjects will be destroyed by calling ~A but still ~B will not be called.

Only if the constructor of B is completed what you get is a real B instance and then, when destroyed, ~B will be called to execute destruction code.

6502
  • 112,025
  • 15
  • 165
  • 265
1

Let's look at this piece of code:

#include <iostream>
#include <stdexcept>

class A {
public:
    A() {
        std::cout << "A's constructor\n";
    }

    ~A() {
        std::cout << "A's destructor\n";
    }
};

class B {
public:
    B() {
        std::cout << "B's constructor - begin\n";
        throw std::runtime_error("Error");
        std::cout << "B's constructor - end\n";
    }

    ~B() {
        std::cout << "B's destructor\n";
    }

private:
    A a;
};

int main() {
    try {
        B b;
    } catch(const std::runtime_error& exc) {
        std::cerr << exc.what() << '\n';
    }
}

Here's the program's output:

A's constructor
B's constructor - begin
A's destructor
Error

In general, when an object is constructed first its fileds's constructors are invoked and then the constructor for the object is executed. For every successfully executed constructor there must be a destructor called. The destructors are called in reverse order. If a constructor fails then there's no destructor called but if during construction of an object some or all of its fields are constructed then they will be destroyed.

Coming back to the example, in the main function I create an object of class B. The object contains a member of class A. When the b object is created, first it's fields (in this case it's the member called a) are constructed - this is the first line of the output. Then the constructor of the b object begins execution (second line of output). The B's constructor throws an exception. Since the contructor of b's field (that is constructor of a) has already been successfully executed, a's destructor must be called - thrid line of output. Now, the b's constructor has not successfully finished its execution so the destructor is not going to be called for b. The last line of output is the effect of exception handling code in the main function.

In this example you can see that when a constructor succeeds then some time later a corresponding destructor is called. However if a constructor fails no destructor for the object will be called.

Successfully executed constructors and destructors are always paired. It's a very general rule and it's valid also for base classes (if any), objects allocated in arrays, objects allocated on stack etc. - even very weird and complicated cases are always handled this way.

navyblue
  • 776
  • 4
  • 8