views:

596

answers:

8

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?

+25  A: 

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.

Steve Jessop
Just an Msdn reference for your post : http://msdn.microsoft.com/en-us/library/ms182331%28VS.80%29.aspx
Braveyard
The first one is indeed a valid reason. It's also known a _two-phase-construction_. If I need something like this, I hide it in the innards of an object. I would never expose this to the users of my class.
sbi
A: 

You use an initialisation method instead of the constructor if the initialiser needs to be called AFTER the class has been created. So if class A was created as:

A *a = new A;

and the initisalizer of class A required that a be set, then obviously you need something like:

A *a = new A;
a->init();
Alexander Rafferty
I don't buy this. You can just call init privately within the last line of the constructor, then. (I didn't down-vote though)
Mike
That's a restatement of the question, not an answer. *Why* would you need to initialise the object after construction?
Mike Seymour
If the constructor relies on the variable a being set, it's happened to me.
Alexander Rafferty
+5  A: 

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.
gspr
+1 I thought about your second argument; indeed I think this is nevertheless considered a bad practice in C++ and it should be made using some helper objects.
mbq
Your second bullet is probably the most common reason why I sometimes use initializer functions. Yes, the constructor could call the init, but that just makes the code less clear.
Jay
+6  A: 

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.

Matthieu M.
I'd go for the alternative debunkment, "I want the possibility to reinitialize my object: implement a copy-and-swap `operator=`"
Steve Jessop
I thought about it, then realized that there may be optimization opportunities. For example, think of the `assign` method on a `vector` object: by using `assign` you re-use the already allocated storage and thus do not require a spurious memory allocation. It's tricky to do it correctly though, so I agree that copy and swap should be used in the general case.
Matthieu M.
@Matthieu: agreed, it's not wrong to have a re-initialize function, it's just not what I'd start with. If nothing else, for "optimization opportunity" read "basic exception guarantee".
Steve Jessop
@Steve: yes we agree, as said "It's tricky to do it correctly" :-) I guess it's one of those cases where using the STL as an example might not have been the wisest choice since it's written by expert programmers, not average ones.
Matthieu M.
@Matthieu: vector::assign *is* a re-initialization method, and follows your advice above.
Roger Pate
+5  A: 

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).

Péter Török
Sounds like you were using singletons. (Don't do that.)
Roger Pate
@Roger, no, these were not Singletons. You could in fact have multiple so called "models" open at the same time, each containing a single dedicated instance of each Service type. I am well aware of the problems with Singletons, but thanks nevertheless :-)
Péter Török
+1  A: 

And also I like to attach a code sample to answer #1 --

Since also msdn says :

When a virtual method is called, the actual type that executes the method is not selected until run time. When a constructor calls a virtual method, it is possible that the constructor for the instance that invokes the method has not executed.

Example : The following example demonstrates the effect of violating this rule. The test application creates an instance of DerivedType, which causes its base class (BadlyConstructedType) constructor to execute. BadlyConstructedType's constructor incorrectly calls the virtual method DoSomething. As the output shows, DerivedType.DoSomething() executes, and does so before DerivedType's constructor executes.

using System;

namespace UsageLibrary
{
    public class BadlyConstructedType
    {
        protected  string initialized = "No";

        public BadlyConstructedType()
        {
            Console.WriteLine("Calling base ctor.");
            // Violates rule: DoNotCallOverridableMethodsInConstructors.
            DoSomething();
        }
        // This will be overridden in the derived type.
        public virtual void DoSomething()
        {
            Console.WriteLine ("Base DoSomething");
        }
    }

    public class DerivedType : BadlyConstructedType
    {
        public DerivedType ()
        {
            Console.WriteLine("Calling derived ctor.");
            initialized = "Yes";
        }
        public override void DoSomething()
        {
            Console.WriteLine("Derived DoSomething is called - initialized ? {0}", initialized);
        }
    }

    public class TestBadlyConstructedType
    {
        public static void Main()
        {
            DerivedType derivedInstance = new DerivedType();
        }
    }
}

Output :

Calling base ctor.

Derived DoSomething is called - initialized ? No

Calling derived ctor.

Braveyard
+1  A: 

More of a special case: If you create a listener, you might want to make it register itself somewhere (such as with a singleton or GUI). If you do that during its constructor, it leaks a pointer/reference to itself which is not yet safe, since the constructor has not completed (and might even fail completely). Assume the singleton that collects all listeners and sends them events when things happen receives and event, and then loops through its list of listeners (one of them is the instance we are talking about), to send them each a message. But this instance is still mid-way in its constructor, so the call can fail in all kinds of bad ways. In this case, it makes sense to have registration in a separate function, which you obviously do not call from the constructor itself (that would defeat the purpose entirely), but from the parent object, after construction has completed.

But that is a specific case, not the general one.

Kdansky
A: 

One more use of such initialization can be in object pool. Basically you just request the object from the pool. The pool will have already some N objects created which are blank. It's the caller now which can call any method he/she likes to set the members. Once caller has done with the object it will tell the pool to destory it. The advantage is until the object is being used the memory will be saved, and the caller can use it's own suitable member method of initializing the object. An object may be serving a lot of purpose but the caller may not need all, and also may not need to initialize all the member of the objects.

Typically think of database connections. A pool can have bunch of connection object, and the caller can fill the username, password etc.

Manoj R