Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the best technique for exiting from a constructor on an error condition in C++

Tags:

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.

like image 812
Jack BeNimble Avatar asked Apr 10 '09 13:04

Jack BeNimble


2 Answers

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.

like image 96
Brian R. Bondy Avatar answered Sep 25 '22 09:09

Brian R. Bondy


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.

like image 31
Adam Rosenfield Avatar answered Sep 25 '22 09:09

Adam Rosenfield