Constructors and member initializer lists
Constructors son funciones miembro no estáticas declaradas con una sintaxis declarativa especial, que se utilizan para inicializar objetos de sus tipos de clase.
|
Un constructor no puede ser una coroutine . |
(since C++20) |
|
Un constructor no puede tener un explicit object parameter . |
(since C++23) |
Contenidos |
Sintaxis
Los constructores se declaran utilizando declaradores de función miembro de la siguiente forma:
class-name
(
parameter-list
(opcional)
)
except
(opcional)
attr
(opcional)
|
|||||||||
| class-name | - | una expresión de identificador , posiblemente seguida de una lista de atributos , y (desde C++11) posiblemente delimitada por un par de paréntesis | ||||||
| parameter-list | - | lista de parámetros | ||||||
| except | - |
|
||||||
| attr | - | (desde C++11) una lista de atributos |
Los únicos especificadores permitidos en los
especificadores de declaración
de una declaración de constructor son
friend
,
inline
,
constexpr
(desde C++11)
,
consteval
(desde C++20)
, y
explicit
(en particular, no se permite ningún tipo de retorno). Nótese que
calificadores cv y de referencia
tampoco están permitidos: la semántica const y volatile de un objeto en construcción solo entra en vigor después de que el constructor más derivado finalice.
La expresión identificadora de class-name debe tener una de las siguientes formas:
- En una declaración friend , la expresión de identificador es un identificador calificado que nombra un constructor .
- En caso contrario, en una declaración de miembro que pertenece a la especificación de miembros de una clase o plantilla de clase:
-
- Para las clases, la expresión identificadora es el injected-class-name de la clase que la contiene inmediatamente.
- Para las plantillas de clase, la expresión identificadora es un nombre de clase que denomina la current instantiation (until C++20) el injected-class-name (since C++20) de la plantilla de clase que la contiene inmediatamente.
- De lo contrario, la expresión de identificador es un identificador calificado cuyo identificador no calificado terminal es el nombre de clase inyectado de su contexto de búsqueda .
Lista de inicialización de miembros
El cuerpo de una
definición de función
de cualquier constructor de la clase
T
, antes de la llave de apertura de la sentencia compuesta, puede incluir la
lista de inicialización de miembros
, cuya sintaxis es el carácter dos puntos
:
, seguido de la lista separada por comas de uno o más
inicializadores-de-miembro
s, cada uno de los cuales tiene la siguiente sintaxis:
| inicializador de miembro | (1) | ||||||||
| inicializador de clase | (2) | ||||||||
inicializador de
paquete de clase
...
|
(3) | (desde C++11) | |||||||
|
(desde C++11) |
-
Una clase base directa o una
virtual base class
de
T. En este caso el subobjeto de la clase base correspondiente se inicializa directamente con initializer .
| member | - | un identificador que nombra un miembro de datos |
| class | - | un nombre de clase |
| class-pack | - | un paquete que se expande a cero o más clases |
| initializer | - |
un
inicializador
que no comienza con
=
|
struct S { int n; S(int); // declaración del constructor S() : n(7) {} // definición del constructor: // ": n(7)" es la lista de inicialización // ": n(7) {}" es el cuerpo de la función }; S::S(int x) : n{x} {} // definición del constructor: ": n{x}" es la lista de inicialización int main() { S s; // llama a S::S() S s2(10); // llama a S::S(int) }
Explicación
Los constructores no tienen nombres y no pueden ser llamados directamente. Se invocan cuando la inicialización tiene lugar, y se seleccionan de acuerdo con las reglas de inicialización. Los constructores sin explicit especificador son constructores de conversión . Los constructores con un constexpr especificador hacen que su tipo sea un tipo literal . Los constructores que pueden ser llamados sin ningún argumento son constructores por defecto . Los constructores que toman otro objeto del mismo tipo como argumento son constructores de copia y constructores de movimiento .
Antes de que comience la ejecución de la sentencia compuesta que forma el cuerpo de la función del constructor, se completa la inicialización de todas las bases directas, bases virtuales y miembros de datos no estáticos. La lista de inicialización de miembros es el lugar donde se puede especificar la inicialización no predeterminada de estos subobjetos. Para bases que no pueden ser inicializadas por defecto y para miembros de datos no estáticos que no pueden ser inicializados por inicialización por defecto o por su inicializador de miembro predeterminado , si lo tienen (desde C++11) , como miembros de tipos referencia y tipos calificados const, se deben especificar inicializadores de miembros. (Nótese que los inicializadores de miembro predeterminados para miembros de datos no estáticos de instanciaciones de plantillas de clase pueden ser inválidos si el tipo del miembro o el inicializador son dependientes.) (desde C++11) No se realiza inicialización para uniones anónimas o miembros variantes que no tengan un inicializador de miembro o inicializador de miembro predeterminado (desde C++11) .
Los inicializadores donde class nombra una clase base virtual son ignorados durante la construcción de cualquier clase que no sea la clase más derivada del objeto que se está construyendo.
Los nombres que aparecen en el initializer se evalúan en el ámbito del constructor:
class X { int a, b, i, j; public: const int& r; X(int i) : r(a) // inicializa X::r para referirse a X::a , b{i} // inicializa X::b al valor del parámetro i , i(i) // inicializa X::i al valor del parámetro i , j(this->i) // inicializa X::j al valor de X::i {} };
Las excepciones que son lanzadas desde inicializadores de miembros pueden ser manejadas por un bloque try de función .
|
Si un miembro de datos no estático tiene un inicializador de miembro predeterminado y también aparece en una lista de inicialización de miembros, entonces se utiliza el inicializador de miembro y se ignora el inicializador de miembro predeterminado: struct S { int n = 42; // default member initializer S() : n(7) {} // will set n to 7, not 42 }; |
(since C++11) |
Los miembros de referencia no pueden vincularse a temporales en una lista de inicialización de miembros:
struct A { A() : v(42) {} // Error const int& v; };
Nota: lo mismo aplica para default member initializer .
Operaciones durante la construcción y destrucción
Las funciones miembro (incluyendo
funciones miembro virtuales
) pueden ser llamadas para un objeto en construcción o destrucción. De manera similar, un objeto en construcción o destrucción puede ser el operando de
typeid
o
dynamic_cast
.
Sin embargo, si estas operaciones se realizan durante cualquiera de las siguientes evaluaciones, el comportamiento es indefinido:
|
(desde C++26) |
- una evaluación de una lista de inicialización de miembro antes de que todos los member-initializer s para clases base hayan completado
Constructor de delegaciónSi el nombre de la clase misma aparece como class-or-identifier en la lista de inicialización de miembros, entonces la lista debe consistir únicamente en ese inicializador de miembro; tal constructor se conoce como delegating constructor , y el constructor seleccionado por el único miembro de la lista de inicialización es el target constructor . En este caso, el target constructor es seleccionado por resolución de sobrecarga y se ejecuta primero, luego el control regresa al delegating constructor y se ejecuta su cuerpo. Los delegating constructors no pueden ser recursivos. class Foo { public: Foo(char x, int y) {} Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char, int) }; Constructores heredadosVer using declaration . |
(desde C++11) |
Orden de inicialización
El orden de los inicializadores de miembros en la lista es irrelevante, el orden real de inicialización es el siguiente:
(Nota: si el orden de inicialización estuviera controlado por la aparición en las listas de inicialización de miembros de diferentes constructores, entonces el destructor no podría garantizar que el orden de destrucción sea el inverso del orden de construcción.)
Notas
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_delegating_constructors
|
200604L
|
(C++11) | Constructores delegados |
Ejemplo
#include <fstream> #include <string> #include <mutex> struct Base { int n; }; struct Class : public Base { unsigned char x; unsigned char y; std::mutex m; std::lock_guard<std::mutex> lg; std::fstream f; std::string s; Class(int x) : Base{123}, // inicializar clase base x(x), // x (miembro) se inicializa con x (parámetro) y{0}, // y inicializado a 0 f{"test.cc", std::ios::app}, // esto ocurre después de que m y lg se inicialicen s(__func__), // __func__ está disponible porque la lista de inicialización es parte del constructor lg(m), // lg usa m, que ya está inicializado m{} // m se inicializa antes que lg aunque aparezca último aquí {} // sentencia compuesta vacía Class(double a) : y(a + 1), x(y), // x se inicializará antes que y, su valor aquí es indeterminado lg(m) {} // el inicializador de clase base no aparece en la lista, se // inicializa por defecto (no es lo mismo que si se usara Base(), que es inicialización por valor) Class() try // el bloque try de función comienza antes del cuerpo de la función, que incluye la lista de inicialización : Class(0.0) // constructor delegado { // ... } catch (...) { // ocurrió una excepción durante la inicialización } }; int main() { Class c; Class c1(1); Class c2(0.1); }
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.
| DR | Se aplica a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 194 | C++98 |
la sintaxis del declarador de constructor solo permitía
como máximo un especificador de función (ej. un constructor no podía declararse inline explicit ) |
se permiten múltiples especificadores
de función |
| CWG 257 | C++98 |
no estaba especificado si una clase abstracta debía
proporcionar inicializadores de miembros para sus clases base virtuales |
se especifica que no es requerido
y dichos inicializadores de miembros se ignoran durante la ejecución |
| CWG 263 | C++98 |
la sintaxis del declarador de constructor
prohibía que los constructores fueran amigos |
se permite que los constructores
sean amigos |
| CWG 1345 | C++98 |
los miembros de unión anónimos sin inicializadores
de miembros por defecto se inicializaban por defecto |
no se inicializan |
| CWG 1435 | C++98 |
el significado de "nombre de clase" en la
sintaxis del declarador de constructor no estaba claro |
se cambió la sintaxis a una sintaxis de
declarador de función especializada |
| CWG 1696 | C++98 |
los miembros de referencia podían inicializarse con temporales
(cuya duración terminaría al finalizar el constructor) |
dicha inicialización
es incorrecta |
Referencias
- Estándar C++23 (ISO/IEC 14882:2024):
-
- 11.4.5 Constructores [class.ctor]
-
- 11.9.3 Inicialización de bases y miembros [class.base.init]
- Estándar C++20 (ISO/IEC 14882:2020):
-
- 11.4.4 Constructores [class.ctor]
-
- 11.10.2 Inicialización de bases y miembros [class.base.init]
- Estándar C++17 (ISO/IEC 14882:2017):
-
- 15.1 Constructores [class.ctor]
-
- 15.6.2 Inicialización de bases y miembros [class.base.init]
- Estándar C++14 (ISO/IEC 14882:2014):
-
- 12.1 Constructores [class.ctor]
-
- 12.6.2 Inicialización de bases y miembros [class.base.init]
- Estándar C++11 (ISO/IEC 14882:2011):
-
- 12.1 Constructores [class.ctor]
-
- 12.6.2 Inicialización de bases y miembros [class.base.init]
- Estándar C++98 (ISO/IEC 14882:1998):
-
- 12.1 Constructores [class.ctor]
-
- 12.6.2 Inicialización de bases y miembros [class.base.init]