Initialization of 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.
Initializer list syntax
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 output of this example:
B1::B1(int)
B2::B2()
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 cases 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 items:
B::r
to refer toB::x
- Class
A
with the value of the argument toB(int i)
B::j
with the value ofB::i
B::i
with the value of the argument toB(int i)
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 result: 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;
}
See 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. If the delegating constructors feature is enabled, initialization
can only be done within the non-delegating constructor. In other words,
a delegating constructor cannot both delegate and initialize. Consider
the following example:
struct A{
int x,y;
A(int x):x(x),y(0){}
/* the following statement is not allowed */
A():y(0),A(42) {}
}
Constructor A()
delegates to A(int
x)
, but A()
also does the initialization,
which is not permitted. The compiler issues an error to indicate the
violation.For more information, see Delegating constructors (C++11)