Why use an initialization method instead of a constructor?

45,900

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.

Solution 3

Others have listed lots of possible reasons (and proper explanations of why most of these are generally not a good idea). Let me post one example of a (more or less) valid use of init methods, which actually has to do with timing.

In a previous project, we had lots of Service classes and objects, each of which were part of a hierarchy, and cross referencing each other in various ways. So typically, for creating a ServiceA, you needed a parent service object, which in turn needed a service container, which already depended on the presence of some specific services (possibly including ServiceA itself) at initialization time. The reason was that during initialization, most of the services registered itself with other services as listeners to specific events, and/or notified other services about the event of successful initialization. If the other service did not exist at the time of notification, the registration did not happen, thus this service would not receive important messages later, during the usage of the application. In order to break the chain of circular dependencies, we had to use explicit initialization methods separate from constructors, thus effectively making global service initialization a two-phase process.

So, although this idiom should not be followed in general, IMHO it has some valid uses. However, it is best to limit its usage to the minimum, using constructors whenever possible. In our case, this was a legacy project, and we didn't yet fully understand its architecture. At least the usage of init methods was limited to the service classes - regular classes were initialized via constructors. I believe there might be a way to refactor that architecture to eliminate the need for service init methods, but at least I did not see how to do it (and to be frank, we had more urgent issues to deal with at the time I was part of the project).

Solution 4

Two reasons I can think of off the top of my head:

  • Say creating an object involves lots and lots of tedious work that can fail in lots and lots of horrible and subtle ways. If you use a short constructor to set up rudamentary things that won't fail, and then have the user call an initialization method to do the big job, you can at least be sure that you have some object created even if the big job fails. Maybe the object contains information about precisely what way the init failed, or maybe it's important to keep unsuccessfully initialized objects around for other reasons.
  • Sometimes you might want to reinitialize an object long after it has been created. In this way, it's just a matter of calling the initialization method again without destroying and recreating the object.

Solution 5

init() function are good when your compiler doesn't support exceptions, or your target application cannot use a heap (exception are usually implemented using a heap to create and destroy them).

init() routines are also useful when the order of construction needs to be defined. That is to say, if you globally allocate objects, the order in which the constructor is invoked is not defined. For instance:

[file1.cpp]
some_class instance1; //global instance

[file2.cpp]
other_class must_construct_before_instance1; //global instance

The standard provides no guarantee that must_construct_before_instance1's constructor will be invoked before instance1's constructor. When it's tied to hardware, order in which things initialize can be crucial.

Share:
45,900
bastibe
Author by

bastibe

I am an scientist / engineer with specialization in signal processing and audio technology.

Updated on October 18, 2020

Comments

  • bastibe
    bastibe over 3 years

    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?