Конструкция - довольно сложная тема в C ++. Простой ответ - , это зависит . Инициализируется ли Foo или нет, зависит от определения самого Foo. О втором вопросе: как заставить Bar инициализировать Foo: списки инициализации являются ответом.
Хотя общее мнение заключается в том, что Foo будет инициализироваться по умолчанию неявным конструктором по умолчанию (сгенерированным компилятором), который не обязательно должен быть истинным.
Если Foo не имеет заданного пользователем конструктора по умолчанию, Foo будет неинициализирован. Чтобы быть более точным: каждый член Bar или Foo, в котором отсутствует определяемый пользователем конструктор по умолчанию, будет неинициализирован сгенерированным компилятором конструктором по умолчанию Bar :
class Foo {
int x;
public:
void dump() { std::cout << x << std::endl; }
void set() { x = 5; }
};
class Bar {
Foo x;
public:
void dump() { x.dump(); }
void set() { x.set(); }
};
class Bar2
{
Foo x;
public:
Bar2() : Foo() {}
void dump() { x.dump(); }
void set() { x.set(); }
};
template <typename T>
void test_internal() {
T x;
x.dump();
x.set();
x.dump();
}
template <typename T>
void test() {
test_internal<T>();
test_internal<T>();
}
int main()
{
test<Foo>(); // prints ??, 5, 5, 5, where ?? is a random number, possibly 0
test<Bar>(); // prints ??, 5, 5, 5
test<Bar2>(); // prints 0, 5, 0, 5
}
Теперь, если Foo имеет определяемый пользователем конструктор, он будет инициализирован всегда, независимо от того, имеет ли Bar инициализированный пользователем конструктор или нет. Если в Bar есть пользовательский конструктор, который явно вызывает (возможно, неявно определенный) конструктор Foo, то Foo фактически будет инициализирован. Если список инициализации Bar не вызывает конструктор Foo, то это будет эквивалентно случаю, когда у Bar не было определяемого пользователем конструктора.
Тестовый код может нуждаться в пояснении. Нас интересует, действительно ли компилятор инициализирует переменную без кода пользователя, вызывающего конструктор. Мы хотим проверить, инициализирован ли объект или нет. Теперь, если мы просто создадим объект в функции, может случиться так, что он попадет в позицию памяти, которая не была затронута и уже содержит нули. Мы хотим отличить удачу от успеха, поэтому мы определяем переменную в функции и вызываем функцию дважды. При первом запуске он распечатывает содержимое памяти и вызывает изменения. Во втором вызове функции, поскольку трассировка стека одинакова, переменная будет храниться в той же самой позиции памяти. Если бы он был инициализирован, он был бы установлен в 0, иначе он сохранял бы то же значение, что и старая переменная, в точно такой же позиции.
В каждом из тестовых прогонов первое напечатанное значение является инициализированным значением (если оно действительно было инициализировано) или значением в той позиции памяти, которое в некоторых случаях равно 0. Второе значение является просто тестовым токеном. представляет значение в позиции памяти после его изменения вручную. Третье значение происходит от второго запуска функции. Если переменная инициализируется, она возвращается к 0. Если объект не инициализирован, его память сохранит старое содержимое.