Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

Object Initialization

Last updated Jan 1, 2003.

If you’re looking for more up-to-date information on this topic, please visit our C/C++ Programming article, podcast, and store pages.

A few months ago, I explained the rules of POD initialization. This was only one part of the story, as class member initialization rules are quite different. Here, I will discuss these rules and show how to initialize ordinary data members, constants, members of base classes and embedded objects and static data members.

Assignment versus Initialization

Novices — and even experienced programmers — sometimes confuse between initialization and assignment, which are two different concepts. For example, the following constructor doesn't really initialize the data members of an object; it only assigns values to them:

class Person
{
private:
 int age;
 string name;
public:
 Person(int a, const string & n) 
 { //the members age and name are assigned
 age=a; 
 name=n; 
 }
};

When dealing with built-in types, the performance difference between such assignments and proper initialization (which I will demonstrate momentarily) is negligible. However, when it comes to objects, as is the case with the member name for example, the difference can be rather noticeable. This is so because the object in question is first constructed and only then is it assigned.

Member Initialization Lists

A proper initialization of data member has a special syntactic construct called a member initialization list or mem-init for short:

class Person
{
//..
public:
//member initialization list:
 Person(int a, const string & n) : age (a), name (n)
 {}
};

Obligatory Initialization

In the previous example, the initialization of Person's members was optional. Yet there are cases in which you must initialize data members: const members, references, and subobjects (i.e., embedded objects and base classes) whose constructors take arguments require a member initialization list. For example, a class that has a data member of type Person can pass an argument to its constructor like this:

class Foo
{
private:
 int & r; // reference member
 const int MAX; // const member
 Person p; //requires arguments
public:
 explicit Foo(int& n) : r(n), MAX(100), p (MAX,"John D."){}
};

The member initialization list of Foo first initializes the reference r, then the const member MAX and finally it initializes the embedded object p.

Notice that the member MAX, once initialized, serves as an initializer of another member, p. This is perfectly legal because members are initialized according to the order in which they are declared in a class. Thus, changing the initialization list into:

Foo(int& r) : p (MAX,"John D."), MAX(100), r(n) {}

Doesn't change the initialization order which remains: r, MAX and p. The reason is that the compiler automatically transforms each member initialization list so that its members are initialized according to their declarations. Notice, however, that initializing a member x with a member y that is declared after x will cause undefined behavior. Consider the following class in which the member Person is declared before MAX:

class BadFoo
{
private:
 int & r; // reference member
 Person p; //requires arguments
 const int MAX; // const member
public:
 explicit BadFoo(int& n)//MAX used before being initialized
 : r(n), MAX(100), p (MAX,"John D.") {}
};

Don't let the member initialization list mislead you. Although it seemingly initializes MAX before p, the compiler actually transforms the list into:

explicit BadFoo(int& n) //actual initialization order:
 : r(n), p (MAX,"John D."), MAX(100) {}

Consequently, MAX has an indeterminate (i.e. garbage) value when it is passed as an argument to p's constructor. Some compilers issue a warning in this case; others don't. To be on the safe side, always check your initialization lists.

Class Constants

In earlier stages of C++, an anonymous enum was used as a constant shared by all instances of the same class:

class Heap
{
 enum { PAGE_SIZE=2048 };
//...
};

Anonymous enums can still be used as class constants. However, most compilers nowadays support a superior technique that achieves that same goal, namely in-class initialization of const static data members of an integral type:

class Heap
{
 static const int PAGE_SIZE=1024;
public:
 Heap(int pages){ p=new unsigned char[pages*PAGE_SIZE];}
//...
};

This kind of initialization is allowed only for const static members of integral types. For any other static members, provide an initializer in the definition itself:

class Trigo
{
 const static double PI;
//...
};
const double Trigo::PI= 3.14159265358;

Summary

A member initialization list is the primary form of initializing data members of an object. In some cases, initialization is optional. However, const members, references and subobjects whose constructors take arguments necessitate a member initialization list. C++ also defines a special rule for initializing const static members of an integral type. You may initialize such members inside the class body. All other static data members may be initialized only in their definition, outside the class body.