Why use an initialization method instead of a constructor?

I just got into a new company and much of the code base uses initialization methods instead of constructors.

struct MyFancyClass : theUberClass
{
    MyFancyClass();
    ~MyFancyClass();
    resultType initMyFancyClass(fancyArgument arg1, classyArgument arg2, 
                                redundantArgument arg3=TODO);
    // several fancy methods...
};

They told me that this had something to do with timing. That some things have to be done after construction that would fail in the constructor. But most constructors are empty and I don't really see any reason for not using constructors.

So I turn to you, oh wizards of the C++: why would you use an init-method instead of a constructor?


Solution 1:

Since they say "timing", I guess it's because they want their init functions to be able to call virtual functions on the object. This doesn't always work in a constructor, because in the constructor of the base class, the derived class part of the object "doesn't exist yet", and in particular you can't access virtual functions defined in the derived class. Instead, the base class version of the function is called, if defined. If it's not defined, (implying that the function is pure virtual), you get undefined behavior.

The other common reason for init functions is a desire to avoid exceptions, but that's a pretty old-school programming style (and whether it's a good idea is a whole argument of its own). It has nothing to do with things that can't work in a constructor, rather to do with the fact that constructors can't return an error value if something fails. So to the extent that your colleagues have given you the real reasons, I suspect this isn't it.

Solution 2:

Yes I can think of several, but generally it's not a good idea.

Most of the times the reason invoked is that you only report errors through exceptions in a constructor (which is true) whereas with a classic method you can return an error code.

However in properly designed OO-code the constructor is responsible for establishing the class invariants. By allowing a default constructor, you allow an empty class, thus you have to modify the invariants so that is accepted both the "null" class and the "meaningful" class... and each use of the class must first ensure that the object has been properly built... it's crass.

So now, let's debunk the "reasons":

  • I need to use a virtual method: use the Virtual Constructor idiom.
  • There is a lot of work to be done: so what, the work will be done anyway, just do it in the constructor
  • The setup may fail: throw an exception
  • I want to keep the partially initialized object: use a try/catch within the constructor and set the error cause in an object field, don't forget to assert at the beginning of each public method to make sure the object is usable before trying to use it.
  • I want to reinitialize my object: invoke the initialization method from the constructor, you'll avoid duplicate code while still having a fully initialized object
  • I want to reinitialize my object (2): use operator= (and implement it using the copy and swap idiom if the compiler generated version does not suit your need).

As said, in general, bad idea. If you really want to have "void" constructor, make them private and use Builder methods. It's as efficient with NRVO... and you can return boost::optional<FancyObject> in case the construction failed.