8

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?

hookenz
  • 30,814
  • 37
  • 149
  • 251
  • 1
    similar discussion in http://stackoverflow.com/questions/1183531/how-much-work-should-an-object-constructor-do –  Jul 29 '09 at 01:44
  • You should not need to manually clean up your members. This should be realatively automatic with RAII – Martin York Jul 29 '09 at 02:32

5 Answers5

30

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!)

Community
  • 1
  • 1
GManNickG
  • 459,504
  • 50
  • 465
  • 534
  • I'm not actually using the auto_ptr in a container, I'm using the pointer itself in the container. Once the object has been successfully created I don't want it to be deleted by the auto_ptr going out of scope which is why I believe I want to call release to release and transfer ownership somewhere else. And before you ask, I'm using a pointer in the container rather than the object itself in this case is for efficiency purposes. It's actually moved from a queue to a map structure once sent to the server so using a pointer is more efficient here. – hookenz Jul 29 '09 at 05:15
  • Ah. I would suggest using a `boost::shared_ptr`, or `boost::ptr_container`. http://www.boost.org/doc/libs/1_39_0/libs/ptr_container/doc/ptr_container.html – GManNickG Jul 29 '09 at 05:19
  • Could you clarify what you mean by "Concerning your example, no."? If it means that "dostuff(new P);" leaks if P::P() throws, and that "dostuff(auto_ptr

    (new P));" is safer, I have to disagree.

    – Éric Malenfant Jul 29 '09 at 13:15
  • +1 for encouraging people to use exceptions in constructors. One note, the quote from the standard regarding terminate is for static data, which is not the case shown. – iain Jul 29 '09 at 15:26
  • 1
    Also I don't think you need to to use a `shared_ptr` to ensure the memory is freed if the constructor throws an exception, new will free the memory as the constructor failed in this. If you debug this scenario you will see that the `shared_ptr` constructor never gets called when an exception is thrown so it cannot free the memory. However using a `shared_ptr` is still a good idea encase exceptions are thrown else where – iain Jul 29 '09 at 15:31
  • Are quite a bit of testing in solaris mdb and libumem I have found that it's generall safe to throw an exception in a constructor. All class members get destroyed correctly. If you open files with primitives like fopen in a constructor then you'll have to close those before the exception is thrown. Also a function call like dostuff(new P()) is safe if the P constructor throws an exception. No memory was leaked in my test so I don't appear to need to place it into an auto_ptr beforehand. – hookenz Jul 30 '09 at 03:11
  • Indeed, when a constructor throws an exception, all members are guaranteed to have their destructor's called and the memory for the class freed. – GManNickG Jul 30 '09 at 03:26
8

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.

Community
  • 1
  • 1
Michael Burr
  • 311,791
  • 49
  • 497
  • 724
3

When you throw an exception from a constructor a few things will happen.

1) All fully constructed members will have thir destructors called.
2) The Memory allocated for the object will be released.

To help make things automatic you should not have RAW pointer in your class, one of the standard smart pointers will usually do the trick and the compiler optimization will reduce most of the overhead to practically nothing [or only the work you should have been doing manually anyway].

Passing pointers to Functions

The other thing that I would NOT do; is pass a value to a function as a pointer.
The probelm here is that you are not indicating who owns the object. Without the implicit ownership information it is unclear (like all C functions) who is responcable for cleaning up the pointer.

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

You mention that p is stored in a que and used at some later point. This implies that you are passing ownership of the object to the function. So make this relationship explicit by using std::auto_ptr. By doing so the caller of dostuff() knows that he can not use the pointer after calling dostuff() becuase the act of calling the function will have actually transfered the pointer into the function (ie the callers local auto_ptr will contain a NULL pointer after the call to dostuff() ).

void doStuff(std::auto_ptr<P> p)
{
    // do something with p
    //
    // Even if you do nothing with the RAW pointer you have taken the
    // pointer from the caller. If you do not use p then it will be auto
    // deleted.
}


int main()
{
    // This works fine.
    dostuff(std::auto_ptr<P>(new P));


    // This works just as well.
    std::auto_ptr<P>    value(new P);
    dostuff(value);

    // Here you are guranteed that value has a NULL pointer.
    // Because dostuff() took ownership (and physically took)
    // of the pointer so it could manage the lifespan.
}

Storing pointers in a cointainer

You mention that dostuff() is used to store a list of p objects for deferred processing.
So this means you are sticking the objects into a container. Now the normal containers do not support std::auto_ptr. But boost does have support for containers of pointers (where the container takes ownership). Also these contairs understand auto_ptr and will automatically transfer ownership from the auto_ptr to the container.

boost::ptr_list<P>   messages;
void doStuff(std::auto_ptr<P> p)
{
    messages.push_front(p);
}

Note when you access members of these containers it always returns a reference (not a pointer) to the contained object. This is indicating that the lifespan of the object is tied to the lifespan of the container and the reference is valid as long as the container is valid (unless you explicitly remove the object).

Martin York
  • 234,851
  • 74
  • 306
  • 532
3

The previous answers have been excellent. I just want to add one thing, based on Martin York's and Michael Burr's answers.

Using the example constructor from Michael Burr, I've added an assignment in the constructor body:

C::C(int ii, double id)
try
     : i(f(ii)), d(id)
{
     //constructor function body

     d = sqrt(d);

}
catch (...)
{
     //handles exceptions thrown from the ctor-initializer
     //and from the constructor function body
}

The question now is, when is d considered 'fully constructed', so that its destructor will be invoked if an exception is thrown within the constructor (as in Martin's post)? The answer is: after the initializer

 : i(f(ii)), d(id)

The point is your object's fields will always have their destructors invoked if an exception is thrown from the constructor body. (This is true whether or not you've actually specified initializers for them.) Conversely, if an exception is thrown from another field's initializer, then destructors will be invoked for those fields whose initializers have already run (and only for those fields.)

This implies that the best practice is not to allow any field to reach the constructor body with an undestructable value (e.g., an undefined pointer.) That being the case, it's best to actually give your fields their real values through the initializers, rather than (say) first setting the pointers to NULL, and then giving them their 'real' values in the constructor body.

Dan Breslau
  • 11,114
  • 2
  • 33
  • 42
0

If you take a resource in a constructor, such as a socket etc, then this would be leaked if you threw an exception would it not?

But I guess this is the argument for doing no work in a constructor, lazy initializing your connections as you need them.

Spence
  • 26,960
  • 13
  • 61
  • 100
  • 5
    Presumably you would clean up your resources when throwing from a constructor just as you would when throwing from anywhere else. – Mike Daniels Jul 29 '09 at 01:08
  • 3
    When an exception is thrown the stack is unwound, and this would include member variables of the class. As long as the socket practiced RAII, then it would be closed properly. (akin to `auto_ptr`) – GManNickG Jul 29 '09 at 01:15
  • Assuming you did it properly I'd agree with that. His question was where it could be dangerous, I was just throwing out an idea. – Spence Jul 29 '09 at 01:49
  • 2
    Naw. This is a red hearing. All fully constructed members will have their destructors called. So if a socket object had been constructed its destructor will be called. If this is the socket object then you should be tidying up as part of the throw processes. – Martin York Jul 29 '09 at 02:29
  • RAII. I'm not a C++ programmer on my day job, so it's nice to be introduced to good concepts. – Spence Jul 29 '09 at 02:34
  • Yea. RAII is replaced with garbage collection in other languages, which is rather than clean up after yourself, just toss it on the ground and we'll pick it up eventually. – GManNickG Jul 29 '09 at 03:56
  • 1
    not quite. .Net disposer pattern is deterministic in terms of resources, just not memory. – Spence Jul 29 '09 at 04:33
  • 2
    RAII is NEVER replaced with GC. GC only reclaims memory (or resources with very similiar characteristics to memory: temporary files with names that aren't significant, maybe). – Daniel Earwicker Jul 29 '09 at 16:49
  • 1
    A lot of languages with GC have RAII (they just don't call it that). – Daniel Earwicker Jul 29 '09 at 16:51