Order of execution in constructor initialization list

前端 未结 3 1023
天涯浪人
天涯浪人 2020-11-28 10:43

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

3条回答
  •  遥遥无期
    2020-11-28 11:35

    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(impl_))
    { 
    } 
    

    Instead, just put the extra work into the constructor:

    X::X() : impl_(Allocate(sizeof(X_Implementation)))
    { 
        impl_ = Construct(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(impl_);
        }
    
        ~X()
        {
            Destruct(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

提交回复
热议问题