Using the initializer list, the members are created and initialized only once, with the given value.
Using assignment, the members are initialized with a default value, then reassigned in the constructor body.
In some cases (constants, references) you can only use initialization lists because
- these must be initialized with a valid value, and
- once they are constructed, reassigning them is illegal.
In other cases, even though assignment is possible, initialization list is still preferable as it avoids extra work (double initialization, which can be costly for some types) and adheres to a common idiom, making your code easier to understand and maintain.
One caveat which is good to know is that the order of initialization of members is defined by their order of declaration, not by the order in which they are listed in the initialization list. E.g.
class Example {
int a, b, c;
Example() : a(1), c(2), b(c) {}
}
yields undefined behaviour because b
is initialized before c
, hence with an undefined value. To avoid confusion and the potential for such subtle bugs, always list members in the initialization list in the same order as they're declared in the class.
This may seem obscure at first, but there is a reason for it. In C++, it is guaranteed that all members of a class are destroyed in exactly the reverse of the order they were created. Now, classes can have multiple constructors, each with its own initialization list, and (unfortunately, one might say) initialization lists can be ordered any way the programmer wants. If the order of the initialization list determined the actual order of initialization, the runtime should somehow maintain data about each object to remember which constructor it was created with, and in what order its members should be destroyed. This would incure runtime overhead, for no obvious benefit, so - in line with the general C++ philosophy of "pay only for what you use" - it was decided that the initialization order is defined by the declaration order once and for all.