Если у меня есть класс, который объединяет другие объекты, которые имеют взаимозависимости, (как) я должен применять их порядок построения?
Например.
class Parent
{
Child1 c1;
Child2 c2;
};
Представьте, что конструктор Child2 требует Child1& и я хочу передать в c1 конструкторам c2.
Если я просто сделаю следующее …
Parent::Parent()
: c2(c1)
{
}
…это может быть нехорошо, так как c1 не может быть создан ко времени запуска инициализатора для c2? Или это достаточно хорошо, что c1 стоит перед c2 в объявлении класса?
Или я должен явно ссылаться на конструктор c1 (если это не обязательно, то рекомендуется ли делать это, чтобы сделать его явным?). Например.
class Parent
{
Child1 c1;
Child2 c2;
};
Parent::Parent()
: c1()
: c2(c1)
{
}
Элементы всегда создаются в том порядке, в котором они объявлены. Так что если у вас есть:
class Parent
{
Child1 c1;
Child2 c2;
};
вам гарантировано, что c1
будет построен раньше c2
, Так что если c2
нужен Child1&
тогда это совершенно четко определено:
Parent::Parent()
: c2(c1)
{
}
c1
будет построен по умолчанию, а затем c2
будет построен с определенно уже построен c1
,
Все это не имеет никакого эффекта. Переменные-члены создаются в порядке их объявления. Полная остановка.
Вот полное объяснение от cppreference:
Порядок инициализаторов элементов в списке не имеет значения: фактический порядок инициализации следующий:
- Элемент списка
Если конструктор предназначен для самого производного класса, виртуальные базовые классы инициализируются в том порядке, в котором они отображаются в порядке обхода слева направо в объявлениях базового класса (слева направо относится к появлению в базе). списки спецификаторов)- Затем прямые базовые классы инициализируются в порядке слева направо, как они появляются в списке базовых спецификаторов этого класса.
- Затем нестатические члены-данные инициализируются в порядке объявления в определении класса.
- Наконец, тело конструктора выполняется
Порядок объявления в классе является единственной важной вещью. Порядок построения в списке инициализатора не соблюдается компилятором, действительно, вы можете включить предупреждение, предупреждающее вас об этом факте (порядок в списке инициализатора! = Порядок эффективной конструкции).
В c ++ члены в списке инициализаторов инициализируются не в том порядке, в котором вы их поместили в список, а в том порядке, в котором вы их объявили. Фактически, g ++ выдаст предупреждение, если вы не инициализируете элементы в том порядке, в котором вы их объявили. Таким образом, вы должны заботиться о объявлять члены в их логическом порядке — от более низкого уровня до более высокого уровня объекта.
Данные члена строятся в порядке объявления. Если c1
объявлен ранее c2
(как в ваших примерах), тогда он будет построен первым.
Однако между вашими двумя примерами есть небольшая разница:
Parent::Parent()
// c1 is implicitly default-initialized
: c2(c1)
{
}
Parent::Parent()
: c1(), //c1 is value-initialized
c2(c1)
{
}
Если Child1
это тип класса не POD, оба они эквивалентны, но в противном случае вы получите неопределенное значение для c1
,
Если это важно для вас, вы можете прочитать о разнице между дефолт- а также Значение инициализация.