Class Construction

I’ll start with a story. Years ago I was starting out at a little software shop, trying to learn my way around the existing code base. As is usual in these cases, everything I encountered looked bad to me. As I’ve grown a bit as a programmer I’ve learned to realize that some of this feeling comes from simply not understanding the constraints on a given system, and some of this comes from actually encountering bad software. So of course many spirited discussions followed wherein the head of software would defend the code and I would try to tear it down. I think one of the primary goals of a library is that it should be “easy to use correctly and hard to use incorrectly” – Scott Meyers (see below). While I admit that this is not always achievable, it should at least be a goal. He stated emphatically that his library had done a good job of this. So imagine my revulsion when I stumbled upon something like this:

class Some_class {
    bool initialize();
    bool startup();
    void set_some_member( const std::vector<int>& member );

Have a longer look at this one and ask yourself if this is easy to use correctly. If you think it is then look again and ask yourself how to create an instance of Some_class. I asked this exact question and he told me it was obvious and I should look for some examples. So I did. I learned that the correct way to create an instance of Some_class is to first call the constructor (obviously), then call set_some_member (huh?), and then call startup. Also, for the love of God, whatever you do, never call initialize because that leads to undefined behavior.

So this leads me to the point of this post, which is that of all the things your object needs to do, construction is really fundamental. As a corollary to this, initialization functions are awful. Now I can already hear the more senior types pointing out that there are cases where a constructor alone simply cannot do the job. You’re correct, that is true, and in time we’ll be looking at some of those. However there are a whole other set of incorrect cases that junior developers mistakenly think justify initialization functions (and other horrors). We need to end this misconception.

The most pervasive incorrect case is when someone thinks that because there’s no way to return a status code from a constructor, you need an initializer function to truly bring your object to life. In this anti-pattern the constructor does the basic member initialization, which can not fail, and then the initializer does the dangerous work, returning a success code if everything went okay. This leads to two major problems. The first is for the users of this class. They will skim your documentation just enough to find some function they like, and the constructor, and immediately set about creating it and using it. Maybe this will kind of work without calling the initalizer, or maybe it won’t. When something eventually fails, they’ll be annoyed. But more importantly, this object will be hard for you to write because you’ll invariably have to hold some internal flag to check whether or not initialize has been called. Then you’ll need to worry about what happens if someone calls initialize twice, and blah blah blah it’s just not worth it.

In order to stay out of this quagmire you need to know one simple thing: it is correct to throw exceptions from constructors. Many of the junior programmers I speak with don’t know this simple fact. Often they’ll hedge their bets by telling me the code will compile, but it’s not the correct thing to do. They’re wrong. Throwing exceptions from a failed constructor is exactly the correct thing to do. In fact it’s the only way to safely let the calling function know about failed construction. But then what’s the state of an object that throws an exception halfway through construction? Simple, it doesn’t exist, it never did. Objects don’t exist until construction is finished, so if construction doesn’t finish, the object doesn’t exist. Will the destructor be called?  No, it most certainly will not. How can you destruct an object that was never constructed? Another question I often get is, doesn’t this lead to memory leaks?  The answer is: only if you’ve written a constructor that isn’t exception safe. But this has nothing to do with constructors and is simply a fact of writing any function that isn’t exception safe. You should never have a function that allocates anything and just assumes that no exception will ever be called.

C++11 Note: In C++11 we now have delegating constructors. This means a constructor can call another constructor. This makes it a little less clear about when an object is constructed. The rule is that when the first constructor is finished, the object has been created. So now if an exception is thrown from the calling constructor (after the called constructor is finished) the destructor will be called.

So there you have it, end of lesson. Here’s the summary in simple bullet points:

  • Never ever ever ever write an initialization function
  • If something goes wrong during construction, throw an exception
  • If an exception is thrown during construction, the object never existed (the destructor won’t be called)
  • Don’t write functions that aren’t exception safe (constructors or otherwise)

Further reading:

Leave a Reply

Your email address will not be published. Required fields are marked *