With C++ classes you have the option of initializing the class's data members in the constructor's initializer list. Let's say, for example, that you had a class, SomeClass, with integer data members called x, y, and z. You could initialize those data members in a default constructor like this:
SomeClass::SomeClass()
{
x = 0;
y = 0;
z = 0;
}
This is something you commonly see in C++ programming.
Now, look at the same default constructor using an initializer list:
SomeClass::SomeClass() :
x(0), y(0), z(0)
{
}
If you haven't seen an initializer list used before, this might look odd to
you. The initializer list follows the colon in a constructor's definition. The
initializer list is actually a more efficient way of initializing a class's
data members. When member variables are constructed in an initializer list,
they're constructed with the given argument. However, if the initializer list
is omitted, the member variables are first constructed with their default
constructors, and then explicitly assigned in the code. Thus, while it doesn't
matter much for built-in data types, like int, initializer lists are more
efficient for any other data type.
Here's another example. Let's say that SomeClass had another constructor
defined as follows:SomeClass(int _x, int _y, int _z);The definition of this constructor would look like this when an initializer list was implemented:
SomeClass::SomeClass(int _x, int _y, int _z) :
x(_x), y(_y), z(_y)
{
}
If your class is derived from another class, then the initializer list
typically follows the call to the base class constructor. Note that if the base
class constructor is omitted, then the compiler will insert a call to the base
class' default constructor. If SomeClass were derived from a class called Base,
then the default constructor would look like this:
SomeClass::SomeClass() :
Base(), x(0), y(0), z(0)
{
}
Most of the time, use of the initializer list is optional. In some cases,
however, you must use the initializer list. One such case is when you have a
reference to a class as a data member. Take this class for example:
class MyRegistry {
private:
TRegistry& registry;
public:
MyRegistry();
};
This class has a reference to a VCL TRegistry object as a class member. You
might think to initialize the registry member like this:
MyRegistry::MyRegistry()
{
registry = *new TRegistry;
}
This code, however, will result in a compiler error. The only way to initialize
the registry data member is in the initializer list:
MyRegistry::MyRegistry() :
registry(*new TRegistry)
{
}
Because registry is a reference to a TRegistry object, you must initialize it
in the initializer list.
Another example is when you have a member variable (or base class) that doesn't
have a default constructor. In this case, you must construct the member (or the
base class) in the initialization list by calling its appropriate
constructor.
One subtlety that deserves mention is the order of initialization. While it seems logical that member variables are constructed in the order they appear in the initializer list, this is not the case. Instead, member variables are always constructed in the order in which they're declared in the class. Likewise, they're always destructed in the reverse order of construction. This can be very important, especially if the initialization of one member variable is dependent on the initialization of another.
Initializer lists are more than just syntactical sugar. In this article, we showed that they represent the preferred (and, in some cases, the only) way of initializing a class.