Will Exit() or an Exception Prevent an End-Of-Scope Destructor from Being Called

Will exit() or an exception prevent an end-of-scope destructor from being called?

If you call exit, the destructor will not be called.

From the C++ standard (§3.6.1/4):

Calling the function

void exit(int);

declared in <cstdlib> (18.3) terminates the program without leaving the current block and hence without destroying any objects with automatic storage duration (12.4). If exit is called to end a program during the destruction of an object with static storage duration, the program has undefined behavior.

Are destructors run when calling exit()?

No, most destructors are not run on exit().

C++98 §18.3/8 discusses this.

Essentially, when exit is called static objects are destroyed, atexit handlers are executed, open C streams are flushed and closed, and files created by tmpfile are removed. Local automatic objects are not destroyed. I.e., no stack unwinding.

Calling abort lets even less happen: no cleanup whatsoever.

C++: If an exception is thrown, are objects that go out of scope destroyed?

Yes.

C++ Standard n3337

15 Exception handling

§ 15.2 Constructors and destructors

1) As control passes from a throw-expression to a handler, destructors
are invoked for all automatic objects constructed since the try block
was entered
. The automatic objects are destroyed in the reverse order
of the completion of their construction.

2) An object of any storage duration whose initialization or
destruction is terminated by an exception will have destructors
executed for all of its fully constructed subobjects (excluding the
variant members of a union-like class), that is, for subobjects for
which the principal constructor (12.6.2) has completed execution and
the destructor has not yet begun execution. Similarly, if the
non-delegating constructor for an object has completed execution and a
delegating constructor for that object exits with an exception, the
object’s destructor will be invoked. If the object was allocated in a
new-expression, the matching deallocation function (3.7.4.2, 5.3.4,
12.5), if any, is called to free the storage occupied by the object.

3) The process of calling destructors for automatic objects
constructed on the path from a try block to a throw-expression is
called “stack unwinding.” If a destructor called during stack
unwinding exits with an exception, std::terminate is called (15.5.1).
[ Note: So destructors should generally catch exceptions and not let
them propagate out of the destructor. — end note ]

example:

SomeClass c;              // declared before try{} so it is
// still valid in catch{} block
try {
SomeClass t;
throw;
} catch( ...) {
// t destroyed
// c valid
}

Under what circumstances are C++ destructors not going to be called?

Are there any other circumstances where they[destructors] will not be called?

  1. Long jumps: these interfere with the natural stack unwinding process and often lead to undefined behavior in C++.
  2. Premature exits (you already pointed these out, though it's worth noting that throwing while already stack unwinding as a result of an exception being thrown leads to undefined behavior and this is why we should never throw out of dtors)
  3. Throwing from a constructor does not invoke the dtor for a class. This is why, if you allocate multiple memory blocks managed by several different pointers (and not smart pointers) in a ctor, you need to use function-level try blocks or avoid using the initializer list and have a try/catch block in the ctor body (or better yet, just use a smart pointer like scoped_ptr since any member successfully initialized so far in an initializer list will be destroyed even though the class dtor will not be called).
  4. As pointed out, failing to make a dtor virtual when a class is deleted through a base pointer could fail to invoke the subclass dtors (undefined behavior).
  5. Failing to call matching operator delete/delete[] for an operator new/new[] call (undefined behavior - may fail to invoke dtor).
  6. Failing to manually invoke the dtor when using placement new with a custom memory allocator in the deallocate section.
  7. Using functions like memcpy which only copies one memory block to another without invoking copy ctors. mem* functions are deadly in C++ as they bulldoze over the private data of a class, overwrite vtables, etc. The result is typically undefined behavior.
  8. Instantiation of some of smart pointers (auto_ptr) on an incomplete type, see this discussion

Is a destructor called when an object goes out of scope?

Yes, automatic variables will be destroyed at the end of the enclosing code block. But keep reading.

Your question title asks if a destructor will be called when the variable goes out of scope. Presumably what you meant to ask was:

will Foo's destructor be called at the end of main()?

Given the code you provided, the answer to that question is no since the Foo object has dynamic storage duration, as we shall see shortly.

Note here what the automatic variable is:

Foo* leedle = new Foo();

Here, leedle is the automatic variable that will be destroyed. leedle is just a pointer. The thing that leedle points to does not have automatic storage duration, and will not be destroyed. So, if you do this:

void DoIt()
{
Foo* leedle = new leedle;
}

You leak the memory allocated by new leedle.


You must delete anything that has been allocated with new:

void DoIt()
{
Foo* leedle = new leedle;
delete leedle;
}

This is made much simpler and more robust by using smart pointers. In C++03:

void DoIt()
{
std::auto_ptr <Foo> leedle (new Foo);
}

Or in C++11:

void DoIt()
{
std::unique_ptr <Foo> leedle = std::make_unique <Foo> ();
}

Smart pointers are used as automatic variables, as above, and when they go out of scope and are destroyed, they automatically (in the destructor) delete the object being pointed to. So in both cases above, there is no memory leak.


Let's try to clear up a bit of language here. In C++, variables have a storage duration. In C++03, there are 3 storage durations:

1: automatic: A variable with automatic storage duration will be destroyed at the end of the enclosing code block.

Consider:

void Foo()
{
bool b = true;
{
int n = 42;
} // LINE 1
double d = 3.14;
} // LINE 2

In this example, all variables have automatic storage duration. Both b and d will be destroyed at LINE 2. n will be destroyed at LINE 1.

2: static: A variable with static storage duration will be allocated before the program begins, and destroyed when the program ends.

3: dynamic: A variable with dynamic storage duration will be allocated when you allocate it using dynamic memory allocation functions (eg, new) and will be destroyed when you destroy it using dynamic memory allocation functions (eg, delete).

In my original example above:

void DoIt()
{
Foo* leedle = new leedle;
}

leedle is a variable with automatic storage duration and will be destroyed at the end brace. The thing that leedle points to has dynamic storage duration and is not destroyed in the code above. You must call delete to deallocate it.

C++11 also adds a fourth storage duration:

4: thread: Variables with thread storage duration are allocated when the thread begins and deallocated when the thread ends.

Destructor not being called when leaving scope

If you allocate a object using new

obj2= new cl1;

Then unless you call delete on it, its destructor won't be called implicitly.

EDIT: As @David, meantions in comments, One may call destructor of an object explicitly but in my experience there is rarely a need to manually call the destructor unless one is using placement new version of new.

Variables on stack are implicitly cleaned up(by calling their destructors) when their scope ends.

Dynamically allocated objects are not implicitly cleaned, it is the responsibility of the user to clean them up explicitly calling delete.

This is the very reason one should not use raw pointers but use smart pointers.

Is a C++ destructor guaranteed not to be called until the end of the block?

You are OK with this - it's a very commonly used pattern in C++ programming. From the C++ Standard section 12.4/10, referring to when a destructor is called:

for a constructed object with
automatic storage duration
when the block in which the object is
created exits

When do destructors get called when multiple exceptions handlings are involved?

When you catch an exception, the stack is "unwound" between the point where the exception was thrown, and the point where it is caught. This means that all automatic variables in the scopes between those two points are destroyed -- everything inside the try that corresponds to whatever catch matches the exception.

Your object obj is an automatic variable in the main function, outside the try. Hence, it is not destroyed when the stack is unwound. Your code relies on that fact -- after the first catch you call doDivide on it again, so it better not have been destroyed.

If you don't catch the exception at all, then it is implementation-defined whether or not the stack is unwound (15.3/9 in C++11) before the program is terminated. It looks as though in your second test, without any catch clauses, it is not.

This means that if you want your RAII objects to "work", then you can't allow uncaught exceptions in your program. You could do something like:

int main() {
try {
do_all_the_work();
} catch (...) {
throw; // or just exit
}
}

Now you're guaranteed that any automatic variables in do_all_the_work will be destroyed if an exception escapes the function. The downside is that you might get less info out of your debugger, because it has forgotten the original throw site of the uncaught exception.

Of course, it's still possible for code in your program to prevent your obj from being destroyed, for example by calling abort().

If you shouldn't throw exceptions in a destructor, how do you handle errors in it?

Throwing an exception out of a destructor is dangerous.

If another exception is already propagating the application will terminate.

#include <iostream>

class Bad
{
public:
// Added the noexcept(false) so the code keeps its original meaning.
// Post C++11 destructors are by default `noexcept(true)` and
// this will (by default) call terminate if an exception is
// escapes the destructor.
//
// But this example is designed to show that terminate is called
// if two exceptions are propagating at the same time.
~Bad() noexcept(false)
{
throw 1;
}
};
class Bad2
{
public:
~Bad2()
{
throw 1;
}
};

int main(int argc, char* argv[])
{
try
{
Bad bad;
}
catch(...)
{
std::cout << "Print This\n";
}

try
{
if (argc > 3)
{
Bad bad; // This destructor will throw an exception that escapes (see above)
throw 2; // But having two exceptions propagating at the
// same time causes terminate to be called.
}
else
{
Bad2 bad; // The exception in this destructor will
// cause terminate to be called.
}
}
catch(...)
{
std::cout << "Never print this\n";
}

}

This basically boils down to:

Anything dangerous (i.e. that could throw an exception) should be done via public methods (not necessarily directly). The user of your class can then potentially handle these situations by using the public methods and catching any potential exceptions.

The destructor will then finish off the object by calling these methods (if the user did not do so explicitly), but any exceptions throw are caught and dropped (after attempting to fix the problem).

So in effect you pass the responsibility onto the user. If the user is in a position to correct exceptions they will manually call the appropriate functions and processes any errors. If the user of the object is not worried (as the object will be destroyed) then the destructor is left to take care of business.

An example:

std::fstream

The close() method can potentially throw an exception.
The destructor calls close() if the file has been opened but makes sure that any exceptions do not propagate out of the destructor.

So if the user of a file object wants to do special handling for problems associated to closing the file they will manually call close() and handle any exceptions. If on the other hand they do not care then the destructor will be left to handle the situation.

Scott Myers has an excellent article about the subject in his book "Effective C++"

Edit:

Apparently also in "More Effective C++"

Item 11: Prevent exceptions from leaving destructors



Related Topics



Leave a reply



Submit