Порядок выполнения в списке инициализации конструктора

Определяется ли порядок выполнения в списке инициализации конструктора? Я знаю, что порядок членов в классе - это порядок, в котором эти члены будут инициализированы, но если у меня есть такой сценарий:

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
{
}

но для того, чтобы это было надежно, этот порядок ДОЛЖЕН быть слева направо. Это гарантировано GREAT BOOK OF STD:: или нет? Если нет, я всегда могу переместить вторую линию в тело.

3 ответа

Решение

Согласно ISO/IEC 14882:2003(E) раздел 12.6.2:

Инициализация происходит в следующем порядке:

  • Во-первых, и только для конструктора самого производного класса, как описано ниже, виртуальные базовые классы должны быть инициализированы в том порядке, в котором они отображаются при обходе слева направо по глубине направленного ациклического графа базовых классов, где "слева" -to-right "- порядок появления имен базовых классов в списке базовых спецификаторов производного класса.
  • Затем прямые базовые классы должны быть инициализированы в порядке объявления, как они появляются в списке базовых спецификаторов (независимо от порядка mem-initializer).
  • Затем нестатические элементы данных должны быть инициализированы в том порядке, в котором они были объявлены в определении класса (опять же, независимо от порядка mem-инициализаторов).
  • Наконец, тело конструктора выполняется.

Итак, следуйте этому порядку, и вы получите свой заказ. Также в соответствии со стандартом, порядок предписан как таковой, так что объекты могут быть инициализированы точно в обратном порядке.

Стандарт C++ действительно гарантирует порядок для списков инициализации (стандарт ISO C++ 12.6.2 / 5):

... элементы нестатических данных должны быть инициализированы в том порядке, в котором они были объявлены в определении класса (опять же, независимо от порядка mem-инициализаторов).

(См . Ответ Уайетта Андерсона для получения дополнительной информации.)

Пример:

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!
}

Однако вы не можете инициализировать переменную дважды - то, что у вас есть, не скомпилируется.

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_))
{ 
} 

Вместо этого просто добавьте дополнительную работу в конструктор:

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

Могут быть проблемы безопасности исключений с вышеупомянутым кодом, но не зная, что Allocate() или же Construct() на самом деле я не могу сказать. Я могу вам сказать, что лучше разделить распределение и конструкцию на их собственные классы, если вы сделаете это, используя идиому Resource Acquisition Is Initialization (RAII):

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
    }
};

Таким образом, если Construct() выдает исключение, вы не потеряете память, так как будет вызван деструктор базового класса, который освободит память, указанную impl_, Это важно, потому что, если исключение не перехвачено и покидает конструктор, соответствующий ему деструктор вызываться не будет. См. Статью Бьярна Страуструпа о безопасности исключений: http://www2.research.att.com/~bs/except.pdf

Ваш конкретный сценарий основан на идее инициализации одного и того же члена более одного раза. Это просто незаконно в C++. Ваш код не скомпилируется. Итак, вопрос, который вы задаете, на самом деле не существует.

Порядок инициализации членов - это порядок их объявления в определении класса. В контекстах без наследования, которые охватывают все, что связано с порядком инициализации в списке инициализаторов конструкций.

Другие вопросы по тегам