Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it ever not safe to throw an exception in a constructor?

Tags:

c++

I know that it's not safe to throw exceptions from destructors, but is it ever unsafe to throw exceptions from constructors?

e.g. what happens for objects that are declared globally? A quick test with gcc and I get an abort, is that always guaranteed? What solution would you use to cater for that situation?

Are there any situations where constructors can throw exceptions and not leave things how we expect.

EDIT: I guess I should add that I'm trying to understand under what circumstances I could get a resource leak. Looks like the sensible thing to do is manually free up resources we've obtained part way through construction before throwing the exception. I've never needed to throw exceptions in constructors before today so trying to understand if there are any pitfalls.

i.e. Is this also safe?

class P{
  public:
    P() { 
       // do stuff...

       if (error)
          throw exception
    }
}

dostuff(P *p){
 // do something with P
}

... 
try {
  dostuff(new P())
} catch(exception) {

}

will the memory allocated to the object P be released?

EDIT2: Forgot to mention that in this particular case dostuff is storing the reference to P in an output queue. P is actually a message and dostuff takes the message, routes it to the appropriate output queue and sends it. Essentially, once dostuff has hold of it, it gets released later in the innards of dostuff. I think I want to put an autoptr around P and call release on the autoptr after dostuff to prevent a memory leak, would that be correct?

like image 658
hookenz Avatar asked Jul 29 '09 00:07

hookenz


2 Answers

Throwing exceptions from a constructor is a good thing. When something fails in a constructor, you have two options:

  • Maintain a "zombie" state, where the class exists but does nothing, or
  • Throw an exception.

And maintaining zombie classes can be quite a hassle, when the real answer should have been, "this failed, now what?".

According to the Standard at 3.6.2.4:

If construction or destruction of a non-local static object ends in throwing an uncaught exception, the result is to call terminate (18.6.3.3).

Where terminate refers to std::terminate.


Concerning your example, no. This is because you aren't using RAII concepts. When an exception is thrown, the stack will be unwound, which means all objects get their destructor's called as the code gets to the closest corresponding catch clause.

A pointer doesn't have a destructor. Let's make a simple test case:

#include <string>

int main(void)
{
    try
    {
        std::string str = "Blah.";
        int *pi = new int;

        throw;

        delete pi; // cannot be reached
    }
    catch(...)
    {
    }
}

Here, str will allocate memory, and copy "Blah." into it, and pi will be initialized to point to an integer in memory.

When an exception is thrown, stack-unwinding begins. It will first "call" the pointer's destructor (do nothing), then str's destructor, which will free the memory that was allocated to it.

If you use RAII concepts, you'd use a smart pointer:

#include <memory>
#include <string>

int main(void)
{
    try
    {
        std::string s = "Blah.";
        std::auto_ptr<int> pi(new int);

        throw;

        // no need to manually delete.
    }
    catch(...)
    {
    }
}

Here, pi's destructor will call delete and no memory will be leaked. This is why you should always wrap your pointers, and is the same reason we use std::vector rather than manually allocating, resizing, and freeing pointers. (Cleanliness and Safety)

Edit

I forgot to mention. You asked this:

I think I want to put an autoptr around P and call release on the autoptr after dostuff to prevent a memory leak, would that be correct?

I didn't state it explicitly, and only implied it above, but the answer is no. All you have to do is place it inside of auto_ptr and when the time comes, it will be deleted automatically. Releasing it manually defeats the purpose of placing it in a container in the first place.

I would also suggest you look at more advanced smart pointers, such as those in boost. An extraordinarily popular one is shared_ptr, which is reference counted, making it suitable for storage in containers and being copied around. (Unlike auto_ptr. Do not use auto_ptr in containers!)

like image 191
GManNickG Avatar answered Oct 17 '22 09:10

GManNickG


As Spence mentioned, throwing from a constructor (or allowing an exception to escape a constructor) risks leaking resources if the constructor is not written carefully to handle that case.

This is one important reason why using RAII objects (like smart pointers) should be favored - they'll automatically handle the cleanup in the face of exceptions.

If you have resources that require deleting or otherwise manually releasing, you need to make certain that they're cleaned up before the exception leaves. This is not always as easy as it might sound (and certainly not as easy as letting an RAII object handle it automatically).

And don't forget, if you need to manually handle clean up for something that happens in the constructor's initialization list, you'll need to use the funky 'function-try-block' syntax:

C::C(int ii, double id)
try
     : i(f(ii)), d(id)
{
     //constructor function body
}
catch (...)
{
     //handles exceptions thrown from the ctor-initializer
     //and from the constructor function body
}

Also, remember that exception safety is the main (only??) reason that the 'swap' idiom gained widespread favor - it's an easy way to ensure that copy constructors don't leak or corrupt objects in the face of exceptions.

So, the bottom line is that using exceptions to handle errors in constructors is fine, but it's not necessarily automatic.

like image 41
Michael Burr Avatar answered Oct 17 '22 10:10

Michael Burr