|A problem of order - constructor initialization|
|Written by Alex Armstrong|
Page 2 of 2
The key to finding the problem is to simply check the value of a and b in an instance of the class. The result for example obtained on one run was
This is clearly not right and the first value and the fact it was different each time the program is run suggests that a is being initialized with random crud.
The reason is that the background information provided at the start of this puzzle left out one important point that is often overlooked. The order that initializations are carried out isn't determined by the order that they are written in the initialization list but by the order in which the variables are declared. This is simple enough but when you look at:
MyClass(int value) : b(value*2),a(b)
you can't help but read the instructions in order and you mind just believes that this is the default flow of control. That is the list is equivalent to:
However what the compiler does is read the variable declarations and then apply the initialization you specified in the list. So in fact the declarations are re-written as:
And now you can see quite clearly where the undefined value comes from. Of course if you actually wrote the above you would get a compiler error that b wasn't defined but you usually don't get that error from an initialization list that implies the same thing.
The best way of avoiding this sort of error is not to use constructor initialization lists at all but this is slightly restrictive. Initialization lists are more efficient than writing code into the body of the constructor in the case of object members. The reason is that putting assignments in to the body of the constructor first initializes everything to default values, using their default constructors and then applies the assignment initializers which creates a temporary object and then uses the object's assignment operator. For intrinsic types such as int there is no real difference between the two methods.
Also you cannot initialize const or reference members within the constructor bodies but you can in an initializer list.
A better rule might be not to reference variables being initialized as values of other variables in the list but once again there are time when it seems natural to do so. For example, if you initialize an object and then want to initialize another variable to reference some member of the first object. For example suppose we have:
And then we use this in other class:
This works fine and a is initialized to whatever myObj2.x is initialized to, i.e. 3 in this case. Now consider:
This doesn't work because myObj2 isn't constructed at the point that the attempt is made to set a to myObj2.x.
Sometimes it just seems safer to always initialize in the constructor.
It is certainly worth remembering that the order of initialization is always the same as the order of declaration.