Última actualización:2026-10-12
C++ Idioma de la lista de inicialización de clases¶
- Los miembros const solo pueden inicializarse en la lista de inicialización del constructor
- Los miembros de tipo referencia también solo pueden inicializarse en la lista de inicialización del constructor
- Los miembros objeto (cuando la clase correspondiente al objeto miembro no tiene constructor por defecto) también solo pueden inicializarse en la lista de inicialización del constructor
Anidamiento entre clases¶
Primera forma: usar la lista de inicialización.
class Animal {
public:
Animal() {
std::cout << "Animal() is called" << std::endl;
}
Animal(const Animal &) {
std::cout << "Animal (const Animal &) is called" << std::endl;
}
Animal &operator=(const Animal &) {
std::cout << "Animal & operator=(const Animal &) is called" << std::endl;
return *this;
}
~Animal() {
std::cout << "~Animal() is called" << std::endl;
}
};
class Dog {
public:
Dog(const Animal &animal) : __animal(animal) {
std::cout << "Dog(const Animal &animal) is called" << std::endl;
}
~Dog() {
std::cout << "~Dog() is called" << std::endl;
}
private:
Animal __animal;
};
int main() {
Animal animal;
std::cout << std::endl;
Dog d(animal);
std::cout << std::endl;
return 0;
}
Resultado de la ejecución:
Animal() is called
Animal (const Animal &) is called
Dog(const Animal &animal) is called
~Dog() is called
~Animal() is called
~Animal() is called
Analizando de arriba abajo, en orden:
En la función main, Animal animal; invoca al constructor por defecto.
Dog d(animal); equivale a:
Finalmente se invoca al destructor y termina el programa.
Segunda forma: inicializar el objeto mediante asignación en el constructor.
El constructor se modifica así:
Dog(const Animal &animal) {
__animal = animal;
std::cout << "Dog(const Animal &animal) is called" << std::endl;
}
Animal() is called
Animal() is called
Animal & operator=(const Animal &) is called
Dog(const Animal &animal) is called
~Dog() is called
~Animal() is called
~Animal() is called
De ahí se concluye:
Al llamar a Dog d(animal);, equivale a:
Definir primero el objeto y asignar después; por ello se invoca primero al constructor por defecto y después a la sobrecarga del operador =.
Resumen
De lo anterior obtenemos las siguientes conclusiones:
- Si una clase contiene otras class o struct definidas por nosotros, usar la lista de inicialización equivale en realidad a crear el objeto e inicializarlo a la vez
- En cambio, con la asignación dentro de la clase equivale a definir primero el objeto y asignar después; normalmente se invoca primero al constructor por defecto y luego a la sobrecarga del operador =.
En una relación de herencia sin constructor por defecto¶
Consideremos ahora cambiar la relación anterior por herencia, y modifiquemos los constructores de Animal y Dog con el siguiente código:
class Animal {
public:
Animal(int age) {
std::cout << "Animal(int age) is called" << std::endl;
}
Animal(const Animal & animal) {
std::cout << "Animal (const Animal &) is called" << std::endl;
}
Animal &operator=(const Animal & amimal) {
std::cout << "Animal & operator=(const Animal &) is called" << std::endl;
return *this;
}
~Animal() {
std::cout << "~Animal() is called" << std::endl;
}
};
class Dog : Animal {
public:
Dog(int age) : Animal(age) {
std::cout << "Dog(int age) is called" << std::endl;
}
~Dog() {
std::cout << "~Dog() is called" << std::endl;
}
};
Lo anterior pasa los parámetros al constructor parametrizado de la clase base mediante la lista de inicialización; ¿qué efectos indeseables aparecen si no se pasan por la lista de inicialización?
Quitamos la lista de inicialización
Ejecutamos el programa:Como Animal no tiene constructor por defecto, se produce el error; este tipo de problema es catastrófico y debemos evitarlo a toda costa, pasando los parámetros por la lista de inicialización para construir la clase base.
Miembros de datos const y de tipo referencia dentro de una clase¶
En particular los miembros de tipo referencia, ¡deben inicializarse con la lista de inicialización y no mediante asignación!
Por ejemplo: añadimos miembros privados al Animal anterior y modificamos el constructor:
class Animal {
public:
Animal(int age,std::string name) {
std::cout << "Animal(int age) is called" << std::endl;
}
private:
int &age_;
const std::string name_;
};
Se produce el siguiente error:
Debería cambiarse por lo siguiente:
Animal(int age, std::string name) : age_(age), name_(name) {
std::cout << "Animal(int age) is called" << std::endl;
}
Temas relacionados¶
- C++ Evitar el constructor por defecto | C++那些事
- C++: plantillas de clase antes que plantillas de función | C++那些事
- C++ Idioma copy-and-swap | C++那些事
- C++ Inyección de dependencias | C++那些事
- C++ enum class enumeraciones fuertemente tipadas | C++那些事
- C++ Header-Only frente a separación de implementación | C++那些事
- C++ Idioma de evaluación perezosa | C++那些事
- C++ Idioma de la tabla de consulta | C++那些事
- C++ Punteros a miembros e información de tipos | C++那些事
- C++ Idioma mixin | C++那些事