NameOn
NameOn

Reputation: 51

Order of calling constructors in one case C++

#include <iostream>
struct A
{
    A(){std::cout<<"A()"<<std::endl;}
};

template<typename T>
struct B
{
    A a;
    T b;
    B(){std::cout<<"B()"<<std::endl;}
};

int main()
{
    B<B<B<int> > > Test;
    return 0;
}

The odrer of calling constructors is

A()
A()
A()
B()
B()
B()

And i have no clue why is that. I thought it would be A B A B A B. Could you explain me why?

Upvotes: 5

Views: 92

Answers (4)

stefan
stefan

Reputation: 10345

This is the expected behavior, because member initialization takes place before the body of the constructor. For realizing this, it is helpful to add the member initializers as well:

template<typename T>
struct B
{
    A a;
    T b;
    B()
    :
       a(),
       b()
    {
       std::cout<<"B()"<<std::endl;
    }
};

To fully grasp the order of execution, let's add a dummy integer field. I also added a template to display the nesting. See http://ideone.com/KylQQb for a demo.

#include <cstdio>

struct A
{
    A()
    :
    dummy(printf("Starting to construct A()\n"))
    {
        printf("Fully constructed A()\n");
    }
    int dummy;
};

template <typename T>
struct Nesting;

template <>
struct Nesting<int>
{
    constexpr static int value = 0;
};

template <template <typename> class T, typename I>
struct Nesting<T<I>>
{
        constexpr static int value = 1 + Nesting<I>::value;
};

template<typename T>
struct B
{
    int dummy;
    A a;
    T b;
    B()
    :
    dummy(printf("Starting to construct B() with nesting %d\n", Nesting<B<T>>::value)),
    a(),
    b()
    {
        printf("Fully constructed B() with nesting %d\n", Nesting<B<T>>::value);
    }
};

int main()
{
    B<B<B<int>>> Test;
    return 0;
}

The output of this will be

Starting to construct B() with nesting 3
Starting to construct A()
Fully constructed A()
Starting to construct B() with nesting 2
Starting to construct A()
Fully constructed A()
Starting to construct B() with nesting 1
Starting to construct A()
Fully constructed A()
Fully constructed B() with nesting 1
Fully constructed B() with nesting 2
Fully constructed B() with nesting 3

Upvotes: 0

Luis Colorado
Luis Colorado

Reputation: 12635

First of all, let's analyze what you have here:

  • you have an object Test of class B<B<B<int> > >, which is:

    class B<B<B<int> > > {
        A a;
        B<B<int> > b;
    };
    
  • the second field of Test, Test.b is of class B<B<int> >, which is:

    class B<B<int> > {
        A a;
        B<int> b;
    };
    
  • then you have the second field of Test.b, Test.b.b, which is of class B<int>, which is:

    class B<int> {
        A a;
        int b;
    };
    

so the order of initialization is:

  • A() for Test.a.
  • A() for Test.b.a.
  • A() for Test.b.b.a.
  • no constructor as Test.b.b.b is of type int and has no constructor.
  • B<int>() for Test.b.b.
  • B<B<int> >() for Test.b.
  • B<B<B<int> > >() for Test.

Unfortunatelly, all the three constructors write on output the same thing: B(), but they're different constructors for different classes.

Upvotes: 1

Julian
Julian

Reputation: 1736

This is because member variables must be initialized before the body of the constructor is executed. Consider the following example:

struct A {
    int value;

    // Here we explicitly initialize 'value' with 5
    A() : value(5) { }
};

struct B {
    A a;

    B()
    {
        // This is perfectly valid and would print 5,
        // because 'a' has already been implicitly initialized
        // with its default constructor.
        std::cout << a.value;
    }
};

If this weren't the case, what value would you expect a to have in B's constructor? You'd run into all sorts of issues. Therefore, the default constructor of A must be implicitly called before the body of B().

Essentially, to make it more explicit, this is what is happening:

    // Initialize 'a' before body of constructor
    B() : a()
    {
        std::cout << a.value;
    }

Upvotes: 1

Fatih BAKIR
Fatih BAKIR

Reputation: 4715

This is actually straight forward, if it was like A B A B A B, then you would have trouble if you wanted to access b from the constructor of B, since the order you thought implies that first member a gets instantiated, then ctor runs, then b gets initialized. In reality, every member is instantiated (constructed etc.) first, then, the constructors are called.

Upvotes: 2

Related Questions