Namespaces
Variants

Constructors and member initializer lists

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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 -

especificación dinámica de excepciones

(hasta C++11)

ya sea especificación dinámica de excepciones
o especificación noexcept

(desde C++11)
(hasta C++17)

especificación noexcept

(desde C++17)
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:

  • 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)
1) Inicializa directamente el miembro de datos denominado member con initializer . member solo puede nombrar miembros de datos no estáticos.
2) Inicializa un objeto de clase con initializer . class solo puede nombrar las siguientes clases:
(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 .
3) Inicializa múltiples subobjetos de clase base utilizando una expansión de paquete .
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ón

Si 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 heredados

Ver 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:

1) Si el constructor es para la clase más derivada, las bases virtuales se inicializan en el orden en que aparecen en el recorrido en profundidad de izquierda a derecha de las declaraciones de clases base (izquierda a derecha se refiere a la aparición en las listas de especificadores base).
2) Luego, las bases directas se inicializan en orden de izquierda a derecha como aparecen en la lista de especificadores de base de esta clase.
3) Luego, los miembros de datos no estáticos se inicializan en el orden de declaración en la definición de la clase.
4) Finalmente, se ejecuta el cuerpo del constructor.

(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]

Véase también