La construcción es un tema bastante difícil en C++. La respuesta simple es depende. Si Foo se inicializa o no depende de la definición de Foo. Sobre la segunda pregunta: cómo hacer que Bar inicialice Foo: listas de inicialización son la respuesta.
Aunque el consenso general es que Foo se inicializará por defecto mediante el constructor predeterminado implícito (compilador generado), no es necesario que sea verdadero.
Si Foo no tiene un constructor predeterminado definido por el usuario, entonces Foo no se inicializará. Para ser más precisos: cada miembro de la barra o del Foo carecen de una definida por el usuario constructor por defecto será inicializada por el compilador genera constructor por defecto de la barra:
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
}
Ahora, si Foo tenía un constructor definido por el usuario y luego lo haría inicializarse siempre, independientemente de si Bar tiene o no un constructor inicializado por el usuario. Si Bar tiene un constructor definido por el usuario que explícitamente llama al constructor (posiblemente implícitamente definido) de Foo, entonces Foo se inicializará. Si la lista de inicialización de Bar no llama al constructor Foo, entonces será equivalente al caso en que Bar no tenga un constructor definido por el usuario.
El código de la prueba puede necesitar algunas explicaciones. Estamos interesados en si el compilador inicializa la variable sin que el código de usuario realmente llame al constructor. Queremos probar si el objeto está inicializado o no. Ahora bien, si simplemente creamos un objeto en una función, es posible que golpee una posición de memoria que no se haya tocado y que ya contenga ceros. Queremos diferenciar la suerte del éxito, por lo que definimos una variable en una función y llamamos a la función dos veces. En la primera ejecución, imprimirá los contenidos de la memoria y forzará un cambio. En la segunda llamada a la función, dado que el seguimiento de la pila es el mismo, la variable se mantendrá exactamente en la misma posición de memoria. Si se inicializó, se establecería en 0, de lo contrario mantendría el mismo valor que tenía la variable anterior en exactamente la misma posición.
En cada una de las ejecuciones de prueba, el primer valor impreso es el valor inicializado (si realmente se inicializó) o el valor en esa posición de memoria, que en algunos casos es 0. El segundo valor es solo una prueba token que representa el valor en la posición de memoria después de cambiarlo manualmente. El tercer valor proviene de la segunda ejecución de la función. Si la variable se está inicializando, retrocederá a 0. Si el objeto no se inicializa, su memoria mantendrá los contenidos anteriores.
Estoy familiarizado con C++ y todavía tengo algunas dudas en este sentido cada cierto tiempo. Además, la mayoría de las respuestas indican claramente que se llamará al constructor predeterminado de Foo, y el hecho es que depende de la definición de Foo. ¿Es un constructor predeterminado proporcionado por el usuario o implícito? ¿Tiene algún atributo de miembro privado? La inicialización en C++ no es simple. –
Bastante gracioso que @xtofl solicite al afiche que elimine el mensaje 'Estoy familiarizado con C++' ... Tal vez la mayoría de la gente no esté 'familiarizada' con C++ cuando casi todas las respuestas son incorrectas. De hecho, la inicialización es difícil, algunas de las personas que respondieron han demostrado su conocimiento de C++ @JaredPar, @dirkgently, @David Thornley y, sin embargo, han fallado. –