C++11 Tutorial: New Constructor Features that Make Object Initialization Faster and Smoother

Constructors in C++11 still do what they’ve always done: initialize an object. However, two new features, namely delegating constructors and class member initializers, make constructors simpler, easier to maintain, and generally more efficient. Learn how to combine these new features in your C++ classes.

C++11 introduced several constructor-related enhancements including:

  • Delegating constructors
  • Class member initializers

The interaction between these two features enables you to tailor your classes’ constructors to achieve safer and simpler initialization. In this article, I demonstrate the use of delegating constructors, explain what class member initializers are, and show how to combine both of them in your classes.

Constructive Talks

Earlier variants of C++ lack a mechanism for calling a constructor from another constructor of the same class (constructors of the same class are known as sibling constructors). This limitation is chiefly noticeable in cases where default arguments aren’t an option. Consequently, the class’s maintainer has to write multiple constructors that repeat similar initialization code segments.

 

Thus far, the common workaround has been to define a separate initialization member function, and call it from every constructor:

class C

{

 void init();//comon initialization tasks

 int s;

 T t; //T has a conversion operator to double

public:

 C(): s(12), t(4.5) {init();}

 C(int i) : s(i), t(4.5) {init();}

 C(T& t1) : s(12), t(t1) {init();}

};

This approach has two drawbacks. First, delegating the initialization task to init() is inefficient because by the time init() executes, the data  members of C have already been constructed. In other words, init() merely reassigns  new values to C’s data members. It doesn’t really initialize them. (By contrast, initialization by means of a member initialization list takes place before the constructor’s body executes.)

The second problem is obvious: Every constructor repeats identical bits of initialization code. Not only is it wearisome, it’s also a recipe for future maintenance problems. Every time you modify the class’s members or its members’ initialization protocol, you’d need to update multiple constructors accordingly.

Introducing Target Constructors

C++11 solves this problem by allowing a constructor (known as the delegating constructor) to call another sibling constructor (the target constructor) from the delegating constructor’s member initialization list. For example:

class C //a C11 class with a target constructor

{

 int s;

 T t;  //has a conversion operator to double

public:

//the target constructor

 C(int i, T& t1): s(i), t(t1) {/*more common init code*/}

//three delegating ctors that call the target ctor

 C(): C(12, 4.5) {/*specific post-init code*/}

 C(int i): C(i, 4.5) {/*specific post-init code*/}

 C(T& t1): C(12, t1) {/*specific post-init code*/}

};

The modified class C now defines a fourth constructor: the target constructor. The three delegating constructors call it from their member initialization list to perform the common initialization procedure. Once the target constructor returns, the body of the delegating constructor is executed.

Notice that there are no special syntactic markers for target and delegating constructors. A target constructor is defined as such if another constructor of the same class calls it. Likewise, a delegating constructor is a constructor that calls a sibling constructor in its member initialization list.

From this you can infer that a target constructor can also be a delegating constructor if it calls another target constructor. However, if a constructor delegates to itself directly or indirectly, the program is ill-formed. The following example demonstrates a target constructor that calls another target constructor:

class Z //C++11, a target ctor that’s also a delegating ctor

{

 int j;

 bool flag;

public:

 Z(): Z(5) {} //#1

 explicit Z(int n) :Z(n, false) {} //#2

 Z(int n, bool b): j(n), flag(b) {} //#3

};

Constructor #1 calls the target constructor #2. Constructor #2 is also a delegating constructor since it calls the target constructor #3.

Technically, the use of default arguments could make two of the three constructors of class Z redundant:

class Z //using default arguments to minimize ctors

{

 int j;

 bool flag;

public:

 Z(int n=5, bool b=false): j(n), flag(b) {}

};

However, default arguments aren’t always desirable (or even possible) for various reasons. Therefore, delegating constructors that call a target constructor are a convenient and more efficient alternative to init().

Class Member Initializers

C++11 introduced another related feature called class member initializers that could make the design and implementation of your constructors even simpler.

Note: Class member initializers are also called in-class initializers. I use both terms interchangeably in this article.

In this case too, C++11 follows other programming languages that let you initialize a data member directly in its declaration:

class M //C++11

{

 int j=5; //in-class initializer

 bool flag (false); //another in-class initializer

public:

 M();

};

M m1; //m1.j = 5, m1.flag=false

Under the hood, the compiler transforms every class member initializer (such as int j=5;) into a constructor’s member initializer. Therefore, the definition of class M above is semantically equivalent to the following C++03 class definition:

class M2

{

 int j;

 bool flag;

public:

 M2(): j(5), flag(false) {}

};

If the constructor includes an explicit member initializer for a member that also has an in-class initializer, the constructor’s member initializer takes precedence, effectively overriding the in-class initializer for that particular constructor. Consider:

class M2

{

 int j=7;

public:

 M2(); //j=7

 M2(int i): j(i){}//overrides j’s in-class initializer  

};

M2 m2; //j=7,

M2 m3(5); //j=5

This property of class member initializers makes them useful for defining a “default” value that constructors may override individually. In other words, a constructor that needs to override the default value for a certain member can use an explicit member initializer for it. Otherwise, the in-class initializer takes effect.

Class member initializers can simplify the design of a class’s constructor. You can specify the initializer for a given member directly with its declaration instead of using a target constructor for other constructors to call. If, however, a certain member requires a more complex initialization procedure (for example, its value depends on other data members’ values, or if its value depends on a specific constructor), a combination of traditional constructor member initializers and target constructors does the trick.

Remember that if a target constructor appears in a constructor’s member initialization list, that list shall contain nothing else. No other base or member initializers are allowed in this case.

Let’s look again at the constructors of class C to see how to combine class member initializers and delegating constructors. Recall that the original constructor set of C looks like this:

//a target constructor

C(int i, T& t1): s(i), t(t1) {/*common init code*/}

//three delegating ctors invoke the target

C(): C(12, 4.5) {/*specific post-init code*/}

C(int i): C(i, 4.5) {/*specific post-init code*/}

C(T& t1): C(12, t1) {/*specific post-init code*/}

The member s is initialized to 12 in two out of four constructors. You can therefore use a class member initializer for it, while leaving constructor-dependent initializers in member initializer lists or in the single target constructor:

class C //C11, combine in-class init with a target ctor

class C //C11, combine in-class init with a target ctor

{

 int s=12;

 T t; 

public:

//a target constructor

 C(int i, T& t1): s(i), t(t1) {}

 C(): t( 4.5) {} //s=12, t=4.5

 C(int i): C(i, 4.5) {}//using a target ctor. s=i, t=4.5

 C( T& t1): t( t1) {} //s=12, t=t1

};

Of course, you can use a different combination such as an in-class initializer for the member t instead of s, or use in-class initializers for both. There is no right or wrong here. Rather, the aim is to reduce the amount of repeated initialization code and to minimize the complexity of the constructors while ensuring that data members are initialized properly. Most of all, the aim is to get rid of the init() hack.

Apart from making your code more readable and easier to maintain, delegating constructors and class member initializers offer a hidden bonus. They also improve your constructors’ performance because they literally initialize data members, as opposed to reassigning a new value to them (which is what init()-like member functions actually do).

Therefore, it might be a good idea to go through your existing pre-C++11 code and refactor it with the following guidelines in mind: Simple initializers that occur frequently should become class member initializers in general, whereas ad-hoc initializers should be dealt with by target constructors and member initializer lists.

Danny Kalev is a certified system analyst by the Israeli Chamber of System Analysts and software engineer specializing in C++. Kalev has written several C++ textbooks and contributes C++ content regularly on software developers' sites. He was a member of the C++ standards committee and has a master's degree in general linguistics. He’s now pursuing a PhD in linguistics.

See also:

 

free-webinar-continuous-load-testing-in

 

subscribe-to-our-blog


Close

Add a little SmartBear to your life

Stay on top of your Software game with the latest developer tips, best practices and news, delivered straight to your inbox

By submitting this form, you agree to our
Terms of Use and Privacy Policy

Thanks for Subscribing

Keep an eye on your inbox for more great content.

Continue Reading