Initializing base classes and members (C++ only)

Constructors can initialize their members in two different ways. A constructor can use the arguments passed to it to initialize member variables in the constructor definition:

complx(double r, double i = 0.0) { re = r; im = i; }

Or a constructor can have an initializer list within the definition but prior to the constructor body:

complx(double r, double i = 0) : re(r), im(i) { /* ... */ }

Both methods assign the argument values to the appropriate data members of the class.

Read syntax diagramSkip visual syntax diagramInitializer list syntax
 
      .-,---------------------------------------------------.
      |                    .---------------------------.    |
      V                    V                           |    |
>>-:----+-identifier-+--(----+-----------------------+-+--)-+--><
        '-class_name-'       '-assignment_expression-'
 

Include the initialization list as part of the constructor definition, not as part of the constructor declaration. For example:

#include <iostream>
using namespace std;

class B1 {
  int b;
public:
  B1() { cout << "B1::B1()" << endl; };

  // inline constructor
  B1(int i) : b(i) { cout << "B1::B1(int)" << endl; }
};
class B2 {
  int b;
protected:
  B2() { cout << "B2::B2()" << endl; }

  // noninline constructor
  B2(int i);
};

// B2 constructor definition including initialization list
B2::B2(int i) : b(i) { cout << "B2::B2(int)" << endl; }

class D : public B1, public B2 {
  int d1, d2;
public:
  D(int i, int j) : B1(i+1), B2(), d1(i) {
    cout << "D1::D1(int, int)" << endl;
    d2 = j;}
};

int main() {
  D obj(1, 2);
}

The following is the output of the above example:

B1::B1(int)
B1::B1()
D1::D1(int, int)

If you do not explicitly initialize a base class or member that has constructors by calling a constructor, the compiler automatically initializes the base class or member with a default constructor. In the above example, if you leave out the call B2() in the constructor of class D (as shown below), a constructor initializer with an empty expression list is automatically created to initialize B2. The constructors for class D, shown above and below, result in the same construction of an object of class D:

class D : public B1, public B2 {
  int d1, d2;
public:

  // call B2() generated by compiler
  D(int i, int j) : B1(i+1), d1(i) {
    cout << "D1::D1(int, int)" << endl;
    d2 = j;}
};

In the above example, the compiler will automatically call the default constructor for B2().

Note that you must declare constructors as public or protected to enable a derived class to call them. For example:

class B {
  B() { }
};

class D : public B {

  // error: implicit call to private B() not allowed
  D() { }
};

The compiler does not allow the definition of D::D() because this constructor cannot access the private constructor B::B().

You must initialize the following with an initializer list: base classes with no default constructors, reference data members, non-static const data members, or a class type which contains a constant data member. The following example demonstrates this:

class A {
public:
  A(int) { }
};

class B : public A {
  static const int i;
  const int j;
  int &k;
public:
  B(int& arg) : A(0), j(1), k(arg) { }
};

int main() {
  int x = 0;
  B obj(x);
};

The data members j and k, as well as the base class A must be initialized in the initializer list of the constructor of B.

You can use data members when initializing members of a class. The following example demonstrate this:

struct A {
  int k;
  A(int i) : k(i) { }
};
struct B: A {
  int x;
  int i;
  int j;
  int& r;
  B(int i): r(x), A(i), j(this->i), i(i) { }
};

The constructor B(int i) initializes the following:

You can also call member functions (including virtual member functions) or use the operators typeid or dynamic_cast when initializing members of a class. However if you perform any of these operations in a member initialization list before all base classes have been initialized, the behavior is undefined. The following example demonstrates this:

#include <iostream>
using namespace std;

struct A {
  int i;
  A(int arg) : i(arg) {
    cout << "Value of i: " << i << endl;
  }
};

struct B : A {
  int j;
  int f() { return i; }
  B();
};

B::B() : A(f()), j(1234) {
   cout << "Value of j: " << j << endl;
}

int main() {
  B obj;
}

The output of the above example would be similar to the following:

Value of i: 8
Value of j: 1234

The behavior of the initializer A(f()) in the constructor of B is undefined. The run time will call B::f() and try to access A::i even though the base A has not been initialized.

The following example is the same as the previous example except that the initializers of B::B() have different arguments:

#include <iostream>
using namespace std;

struct A {
  int i;
  A(int arg) : i(arg) {
    cout << "Value of i: " << i << endl;
  }
};

struct B : A {
  int j;
  int f() { return i; }
  B();
};

B::B() : A(5678), j(f()) {
   cout << "Value of j: " << j << endl;
}

int main() {
  B obj;
}

The following is the output of the above example:

Value of i: 5678
Value of j: 5678

The behavior of the initializer j(f()) in the constructor of B is well-defined. The base class A is already initialized when B::j is initialized.

Related information