2010-10-27 2 views
9

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

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

, но для того, чтобы это быть надежным, этот порядок должен быть слева направо. Гарантируется ли это БОЛЬШОЙ КНИГИ std :: или нет? Если нет, я всегда могу переместить вторую линию в тело.

+1

Используйте новое размещение для создания в буфер памяти, который вы ранее выделили. –

+0

Вы не можете инициализировать объект более одного раза, поэтому этот вопрос является нелогичным. Помимо этого, очевидный дубликат [Порядок оценки списка инициализации конструктора] (https://stackoverflow.com/questions/1242830/constructor-initialization-list-evaluation-order) –

ответ

28

В соответствии с ISO/IEC 14882: 2003 (E) раздел 12.6.2:

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

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

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

22

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

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

(см Wyatt Anderson's answer для получения дополнительной информации.)

Пример:

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() фактически делает I» м не могу сказать. Я могу вам сказать, что лучше отделить распределение и строительство в свои классы, если вы сделаете это, используя 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

4

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

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

Смежные вопросы