views:

439

answers:

8

What's the best technique for exiting from a constructor on an error condition in C++? In particular, this is an error opening a file.

Thanks for the responses. I'm throwing an exception. Here's the code (don't know if it's the best way to do it, but it's simple)

// Test to see if file is now open; die otherwise 
if ( !file.is_open() ) {
 cerr << "Failed to open file: " << m_filename << endl;
 throw ("Failed to open file");
}

One think I like about C++ is you don't have to declare thrown exceptions on the method declarations.

+18  A: 

The best suggestion is probably what parashift says. But read my caution note below as well please.

See parashift FAQ 17.2

[17.2] How can I handle a constructor that fails?

Throw an exception.

Constructors don't have a return type, so it's not possible to use return codes. The best way to signal constructor failure is therefore to throw an exception. If you don't have the option of using exceptions, the "least bad" work-around is to put the object into a "zombie" state by setting an internal status bit so the object acts sort of like it's dead even though it is technically still alive.

The idea of a "zombie" object has a lot of down-side. You need to add a query ("inspector") member function to check this "zombie" bit so users of your class can find out if their object is truly alive, or if it's a zombie (i.e., a "living dead" object), and just about every place you construct one of your objects (including within a larger object or an array of objects) you need to check that status flag via an if statement. You'll also want to add an if to your other member functions: if the object is a zombie, do a no-op or perhaps something more obnoxious.

In practice the "zombie" thing gets pretty ugly. Certainly you should prefer exceptions over zombie objects, but if you do not have the option of using exceptions, zombie objects might be the "least bad" alternative.


A word of caution with throwing exceptions in a constructor:

Be very careful though because if an exception is thrown in a constructor, the class's destructor is not called. So you need to be careful about destructing objects that you already constructed before the exception is thrown. The same warnings apply to exception handling in general, but it is maybe a little less obvious when dealing with a constructor.

class B
{
public:
    B()
    {

    }

    virtual ~B()
    {
     //called after D's constructor's exception is called
    }
};

class D : public B
{
public:
    D()
    {
     p = new char[1024];
     throw std::exception("test");
    }

    ~D()
    {
      delete[] p;
      //never called, so p causes a memory leak
    }

    char *p;
};

int main(int argc, char **argv)
{

    B *p;
    try
    {
     p = new D();
    }
    catch(...)
    {

    }


    return 0;
}

Protected/Private constructors with CreateInstance method:

Another way around this is to make your constructor private or protected and make a CreateInstance method that can return errors.

Brian R. Bondy
@Neil Butterworth: I'm trying to remember why, I've had problems with it before with a patch to the CLucene library, something involving derived types. It led to a partially constructed type somehow which caused a pure virtual function call to crash the program using CLucene.
Brian R. Bondy
Updated with the reason why, it can lead to a lot of problems if your constructor doesn't clean up after itself before the exception is called.
Brian R. Bondy
Sorry - I think they all apply equally to using a return from a constructor when the destructor will be called on a probably improperly constructed object, with horrible results. In both cases the answer is to use self-managing objects as class members.
anon
@Neil Butterworth: I agree with you, and I agree that exceptions are better to use than return in constructors now. But I just thought I'd mention they have to be used with caution and that they are not a very clean solution (maybe cleanest but not clean :))
Brian R. Bondy
Constructors, like everything else, need to be exception-safe when exception safety is important. This is best done by having smart pointers and the like; a simple auto_ptr<> will solve that leak. Never have a pure virtual destructor, or a destructor that calls pure virtuals.
David Thornley
Maybe the difference is that if you simply return, then you are consciously deciding that you are going to handle a zombie object. So you will add handling for your partially constructed members.
Brian R. Bondy
@David Thornley: I agree with the smart pointer usage around there, but it is not only memory management I'm talking about. That was just one example. RAII in general for each member is a good solution, but this is not always used unfortunately.
Brian R. Bondy
@David Thornley: Good point about it applying in general as well, I added that.
Brian R. Bondy
Added notes about another method
Brian R. Bondy
There is nothing wrong with the Zombie state. Look at the fstream objects. You don't need an actually zombie test method, a cleaner solution is a cast to void pointer (like fstream) so that you can use in a boolean context (like if statement).
Martin York
@Martin York: Great info, thanks.
Brian R. Bondy
+1  A: 

If you object after the error can not perform its actions - you have to throw. If it can - log you error and change the construction logic.

Mykola Golubyev
+3  A: 

In general, you should throw an exception. The alternative is to have some half-correctly constructed object which the user has to test somehow, which they will inevitably fail to do.

anon
A: 

Throw exception. Take a look here for more information: Handling error in constructor

Naveen
+2  A: 

If the object that you're constructing is invalid due to the error, and needs to be disposed of by the caller, then you pretty much have to throw an exception. This allows the compiler to perform the proper deallocation of resources.

(Writing exception-safe constructors requires a bit of care -- in brief, you need to use the initializer lists wherever you can, rather than using the constructor body -- but it's critical if you have a case like this, where throwing an exception is a significant possibility.)

Dan Breslau
Can you expand a bit on why it's critical to use initializer lists when an exception might be thrown? You seem to be implying that it would be inherently not exception-safe to throw from the constructor body, but I'm not sure that I see why.
Charles Bailey
I didn't mean that a throw from the body is unsafe. What I meant was that when you throw from anywhere in the ctor (body or initializer lists) the dtors will be called for all fields that have been initalized. Suppose the throw happens during a ctor for class member that itself is a class instance.
Dan Breslau
(cnt'd) - the calling ctor will have to destruct any initialized fields, and will have to know *not* to destruct any fields that haven't been initialized yet. The compiler ensures that this happens correctly; it's impossible to get it right in all cases if you bypass the compiler and do it yourself.
Dan Breslau
It's hard to give this a proper treatment in comments ;-) See http://www.gotw.ca/gotw/066.htm for a decent writeup on the issue.
Dan Breslau
A: 

There is only 1 good way to exit from a constructor that is in error, that is to raise an exception.

Is it really an error? are you trying to add too much to the constructor?

Often people will try and roll in some initial interaction into the constructor, like adding the filename to a file constructor. Do you expect it to open that file right away or are you just setting some state, is it different to file.open(filename), is it ok if it fails?

Greg Domjan
+3  A: 

You can throw an exception, as others have mentioned, or you can also refactor your code so that your constructor can't fail. If, for example, you're working on a project where exceptions are disabled or disallowed, then the latter is your best option.

To make a constructor that can't fail, refactor the code that could potentially fail into an init() method, and have the constructor do as little work as possible, and then require all users of the class to call init() immediately after construction. If init() fails, you can return an error code. Make sure to document this in your class's documentation!

Of course, this is somewhat dangerous, since programmers might forget to call init(). The compiler can't enforce this, so tread carefully, and try to make your code fail-fast if init() is not called.

Adam Rosenfield
A constructor can always fail. Anything that has to allocate memory can always fail, particularly in the sort of environment that's likely to not use exceptions.
David Thornley
I'd like to know how a constructor that does nothing, or one that only initializes POD can fail.
Adam Rosenfield
It can fail on memory allocation. new allocates memory and initializes it. Actually, if you pre-allocate the memory and use placement new, you can get a non-throwing constructor.
David Thornley
Saying "Foo *x = new Foo()" and having the memory allocation fail is NOT the constructor failing -- the constructor doesn't even get called. operator new will either throw an std::bad_alloc exception, or return NULL if that if the compiler is so instructed. The constructor called in neither case.
Adam Rosenfield
@Adam Rosenfield: +1 I agree, an empty constructor cannot fail a memory exception will be thrown before the constructor is even called.
Brian R. Bondy
@Adam Rosenfield: Falling off a tall building won't kill you, either, it's the sudden stop at the end that does it. Saying "Foo *x = new Foo()" may result in having an exception thrown. What does it matter in what code?
David Thornley
@David Thornley: Because you're not always falling off a tall building. If you allocate an object on the stack and it has a trivial constructor, that can NOT fail in any way. No memory allocation is involved. We're talking about constructors here, not memory allocation.
Adam Rosenfield
Sorry, this solution has been discussed ad nauseam on the usenet newsgroups that long predate SO and has been found wanting in every way. I'd -1 this except I don't like doing that on answers in threads I'm taking part in.
anon
A: 

The best thing to do is to throw an exception. That's what they're there for, and any attempt to duplicate the behavior you get is likely to fail somewhere.

If you can't use an exception, for some reason, use nothrow. The example in the Standard, 18.4.1.1 clause 9, is:

t* p2 = new(nothrow) T;    // returns 0 if it fails

This is technically a form of placement new, but it should either return a fully formed object or a null pointer that you need to test for, except that nobody will.

If your class can have an object that is there but not properly initialized, you can have a data member that serves as a flag as to whether the class is useful or not. Again, nobody will check that flag in live code.

Bear in mind that, if you really need to have an allocation that is guaranteed not to fail, you need to allocate the memory ahead of time and use placement new, and remove all initialization that might throw to another routine, which somebody will fail to call. Anything that allocates memory can fail, particularly on the more confined sorts of systems that usually don't support exceptions.

Really, the exceptions are the best way to go.

David Thornley