4

I am reading about order of constructor's initialization in the C++ Super-FAQ from the web site The C++ Programming Language. There the following code is presented.

#include <iostream>
class Y {
public:
  Y();
  void f();
};
Y::Y()      { std::cout << "Initializing Y\n"; }
void Y::f() { std::cout << "Using Y\n"; }
class X {
public:
  X(Y& y);
};
X::X(Y& y) { y.f(); }
class Z {
public:
  Z();
protected:
  X x_;
  Y y_;
};
Z::Z() 
  : y_()
  , x_(y_)
{ }
int main()
{
  Z z;
  return 0;
}

The printed sequence from this code is:

Using Y

Initializing Y

Well, I just can not realize how this printing sequence is possible since in the constructor of class Z the instance y_ of Y class is first instanciated for then the instance x_ of X class be so. In other words, how can the printed order be possible which is close related with the used method order if to use the method Y::f() first I need to instanciate one Y which would certantly call its constructor and the print routine std::cout << "Initializing Y\n";.

Community
  • 1
  • 1
Randerson
  • 775
  • 1
  • 5
  • 19

2 Answers2

4

Since X x_ comes before Y y_ in class Z's defintion x_ is constructed and initialized first. It doesn't matter what order you place the members in the initializer list Z::Z() : y_(), x_(y_) {}, x_ is still initialised first.

Since y_'s constructor hasn't been called some internal elements like the vtable won't have been initialised. Actually using this object in X's constructor could result in a segfault. Perhaps this defintion will be better for you.

class Z {
public:
  Z();
protected:
  Y y_;
  X x_;
};

As x_ is defined after y_, y_ will be called first and you can safely use it in x_'s constructor.

You may wish to read the Initialization order section of this article.

Edit: C++ values speed and trusts the programmer, so it makes no attempt to validate the parameters given to it. You can cast a null pointer into a reference, then use the reference.

Y* p = 0;
X x(reinterpret_cast<Y&>(p));

This will compile, and if the compiler doesn't ever need to deference our null pointer it will not fail. If you made f virtual, or tried to access any of the members, then this would cause a segfault.

Owen Delahoy
  • 806
  • 6
  • 22
  • if x_ is constructed and intialized first but there is no default constructor for x. – user1438832 Dec 20 '16 at 11:38
  • This confusion is actually the cause why the gcc prints a warning when you do not respect the order in the initializer list. – koalo Dec 20 '16 at 11:39
  • It doesn't use the default constructor, it still uses the constructor that you specified in the initializer list. It just doesn't execute in the order that you write it. It executes in the order that they are defined in. – Owen Delahoy Dec 20 '16 at 11:40
  • I see your point. Nevertheless, somehow, the method **Y::f()** was called without an instance of the class Y in the initialization process of **x_**, what should not be possible. Am I right? So, how this happen? – Randerson Dec 20 '16 at 11:42
  • 1
    The object given to f is essentially garbage. The object has been allocated but not initialized. Using Y's members, children or vtable will not have been initialized and willresult in undefined behaviour, but in this example you do not use them. – Owen Delahoy Dec 20 '16 at 11:47
2

Your class Z declares two members in this order:

X x_;
Y y_;

However, they are initialised in the opposite order:

Z::Z()
    : y_()
    , x_(y_)
{ }

You need to be careful of this e.g. see this question or this

Changing the order of declaration and initialisation to match gives what you'd expect. You have undefined behavoiur and some compiles will warn you about the mismatch on the oder.

The specific psot you have linked to says "// Bad: should have listed x_ before y_" in a comment and draws out the point

Note that y_ is used (Y::f()) before it is initialized (Y::Y()).

Community
  • 1
  • 1
doctorlove
  • 18,872
  • 2
  • 46
  • 62