Порядок выполнения в списке инициализации конструктора
Определяется ли порядок выполнения в списке инициализации конструктора? Я знаю, что порядок членов в классе - это порядок, в котором эти члены будут инициализированы, но если у меня есть такой сценарий:
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++. Ваш код не скомпилируется. Итак, вопрос, который вы задаете, на самом деле не существует.
Порядок инициализации членов - это порядок их объявления в определении класса. В контекстах без наследования, которые охватывают все, что связано с порядком инициализации в списке инициализаторов конструкций.