views:

95

answers:

3

Is order of execution in constructor initialization list determinable? I know that members order in a class is the order in which those members will be initialized but if I have scenario like this:

class X()
{
X_Implementation* impl_;
};  

and then providing that allocator is available:

X::X():impl_(Allocate(sizeof(X_Implementation)))//HERE I'M ALLOCATING <--1
,impl_(Construct<X_Implementation>(impl_))//AND HERE I'M CONSTRUCTING <--2
{
}

but in order for this to be dependable this order MUST be from left to right. Is it guarantied by GREAT BOOK OF std:: or not? If not I can always move the second line into the body.

+7  A: 

The C++ standard does guarantee an order for initialization lists (ISO C++ Standard 12.6.2/5):

...nonstatic data members shall be initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).

(See Wyatt Anderson's answer for more information.)

Example:

class Foo
{
public:
    Foo();
private:
    A a;
    B b;
    C c;
};

Foo::Foo() : b(), a(), c()
{
    // a is initialized first, then b, then c - NOT b, a, then c!
}

However, you can't initialize a variable twice - what you have won't compile.

class X //() what's with the pair of parentheses you have in your code snippet?
{ 
public:
    X();
private:
    X_Implementation* impl_; 
};   

X::X() : 
    impl_(Allocate(sizeof(X_Implementation))),
    // It is not allowed to initialize a data member twice!
    impl_(Construct<X_Implementation>(impl_))
{ 
} 

Instead, just put the extra work into the constructor:

X::X() : impl_(Allocate(sizeof(X_Implementation)))
{ 
    impl_ = Construct<X_Implementation>(impl_);
}

There may be exception safety issues with the above code, but without knowing what Allocate() or Construct() actually does I'm not able to tell. I can tell you that it's best to separate allocation and construction into their own classes if you do that, using the Resource Acquisition Is Initialization (RAII) idiom:

class XBase
{
protected:
    XBase() : impl_(Allocate(sizeof(X_Implementation)))
    {
    }

    ~XBase()
    {
        if(impl_ != 0) { Deallocate(impl_); } // Or something like this
    }

    X_Implementation* impl_; 
};

class X : private XBase // XBase is an implementation detail
{
public:
    X()
    {
        impl_ = Construct<X_Implementation>(impl_);
    }

    ~X()
    {
        Destruct<X_Implementation>(impl_); // Or something like this
    }
};

This way, if Construct() throws an exception, you won't leak memory since the base class destructor will be called which will deallocate the memory pointed by impl_. This is important because if the exception is not caught and leaves the constructor, its matching destructor will not be called. See Bjarne Stroustrup's paper on exception safety: http://www2.research.att.com/~bs/except.pdf

In silico
+6  A: 

According to ISO/IEC 14882:2003(E) section 12.6.2:

Initialization shall proceed in the following order:

  • First, and only for the constructor of the most derived class as described below, virtual base classes shall be initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base class names in the derived class base-specifier-list.
  • Then, direct base classes shall be initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
  • Then, nonstatic data members shall be initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
  • Finally, the body of the constructor is executed.

So, follow that order, and you'll have your order. Also according to the standard, the order is prescribed as such so that objects can be uninitialized in the precisely reverse order.

Wyatt Anderson
+1  A: 

Your specific scenario is based on the idea of initializing the same member more than once. This is plain illegal in C++. Your code will not compile. So, the question you are asking doesn't really exist.

The order of member initialization is the order of their declaration in class definition. In no-inheritance contexts that covers everything related to the order of initialization in the constructions initializer list.

AndreyT