Constructing an object within an object

Asked by At

I have an object A declared inside a class, I want to initialize the object A when I initialize the object B:

class A{
        int num1;
        string word;

A::A(int _num1, string _word){
    num1 = num1;
    word = _word;

class B{
        char letter;
        A a;

B::B(char _letter, int _num1, string _word){
    letter = _letter;
    a(_num1, _word);

This gives the error: only a function may be called. My question is, how do I have an object inside another object that can be initialized by the outer objects constructor.

2 Answers

songyuanyao On Best Solutions

You should initialize the data member a with member initializer list,

B::B(char _letter, int _num1, string _word) : a(_num1, _word) {
    letter = _letter;

or better

B::B(char _letter, int _num1, string _word) : letter(_letter), a(_num1, _word) {}

Note that when get into the body of the constructor of B, a must have been initialized. Without using member initializer list as above, a will be trying to be initialized with the default constructor, but A doesn't have one. And a(_num1, _word); is just trying to call on a as if it's a functor, but a isn't.

Kerndog73 On

Member initializer lists are just for this purpose. They are used to call the constructor of members as opposed to assigning them. This can be quite a bit faster if default constructing and then assigning is slower than constructing directly. They should be used instead of an assignment in almost all cases. There is no performance benefit to using them to initialize primitives (it's just a matter of style in that case so I usually do it anyway).

This is how you would write your constructor using member initializer lists.

B::B(char _letter, int _num1, string _word)
    : letter(_letter), a(_num1, _word) {}

This can be improved slightly. We can give the parameters the same names as the members. In this case, the compiler will do the "right thing" and initialize the member with the parameter. This also avoids starting identifiers with underscores which is a separate issue.

B::B(char letter, int num1, string word)
    : letter(letter), a(num1, word) {}

One thing to note about member initializer lists is the order of initialization. The members will be initialized in the order that they are declared in the class. In this case, letter will be initialized before a. The order of the init lists has no effect on the initialization order. Consider this example:

B::B(char letter, int num1, string word)
    : a(num1, word), letter(letter) {}

This will initialize letter then a. This is why you should ensure that the order is the same as declared in the class. If you pass the appropriate warning flags to your compiler, it will tell you about this and your IDE might even reorder things for you.