Share via


Inicializadores

Un inicializador especifica el valor inicial de una variable. Se pueden inicializar variables en estos contextos:

  • En la definición de una variable:

    int i = 3;
    Point p1{ 1, 2 };
    
  • Como uno de los parámetros de una función:

    set_point(Point{ 5, 6 });
    
  • Como el valor devuelto de una función:

    Point get_new_point(int x, int y) { return { x, y }; }
    Point get_new_point(int x, int y) { return Point{ x, y }; }
    

Los inicializadores pueden adoptar estos formatos:

  • Una expresión (o una lista de expresiones separadas por comas) entre paréntesis:

    Point p1(1, 2);
    
  • Un signo igual seguido de una expresión:

    string s = "hello";
    
  • Una lista de inicializadores entre llaves. La lista puede estar vacía o puede ser un conjunto de listas:

    struct Point{
        int x;
        int y;
    };
    class PointConsumer{
    public:
        void set_point(Point p){};
        void set_points(initializer_list<Point> my_list){};
    };
    int main() {
        PointConsumer pc{};
        pc.set_point({});
        pc.set_point({ 3, 4 });
        pc.set_points({ { 3, 4 }, { 5, 6 } });
    }
    

Clases de inicialización

Hay varias clases de inicialización, que pueden producirse en distintos puntos de la ejecución de un programa. Las diferentes clases de inicialización no son mutuamente excluyentes; por ejemplo, la inicialización de la lista puede desencadenar la inicialización de un valor y en otras circunstancias, puede desencadenar la inicialización de agregado.

Inicialización cero

La inicialización cero es la configuración de una variable en un valor cero convertido implícitamente al tipo:

  • Las variables numéricas se inicializan en 0 (o 0,0, 0,0000000000, etc.).

  • Las variables char se inicializan en '\0'.

  • Los punteros se inicializan en nullptr.

  • Los miembros de matrices, clases POD, estructuras y uniones se inicializan en un valor cero.

La inicialización cero se realiza en distintos momentos:

  • Al iniciarse el programa, para todas las variables con nombre que tienen duración estática. Estas variables se pueden volver a inicializar posteriormente.

  • Durante la inicialización del valor, para los tipos escalares y de clase POD que se inicializan mediante llaves vacías.

  • Para las matrices en las que solo se inicializa un subconjunto de sus miembros.

A continuación se muestran algunos ejemplos de inicialización cero:

struct my_struct{
    int i;
    char c;
};

int i0;              // zero-initialized to 0
int main() {
    static float f1;  // zero-initialized to 0.000000000
    double d{};     // zero-initialized to 0.00000000000000000
    int* ptr{};     // initialized to nullptr
    char s_array[3]{'a', 'b'};  // the third char is initialized to '\0'
    int int_array[5] = { 8, 9, 10 };  // the fourth and fifth ints are initialized to 0
    my_struct a_struct{};   // i = 0, c = '\0'
}

Inicialización predeterminada

La inicialización predeterminada de clases, estructuras y uniones es la inicialización que utiliza un constructor predeterminado. Se puede ser llamar al constructor predeterminado si no se incluye una expresión de inicialización o mediante la palabra clave new:

MyClass mc1;
MyClass* mc3 = new MyClass;

Si la clase, la estructura o la unión no tiene un constructor predeterminado, el compilador emite un error.

Las variables escalares se inicializan de forma predeterminada cuando se definen sin una expresión de inicialización. Tienen valores indeterminados.

int i1;
float f;
char c;

Las matrices se inicializan de forma predeterminada cuando se definen sin una expresión de inicialización. Cuando una matriz se inicializa de forma predeterminada, sus miembros se inicializan de forma predeterminada y tienen valores indeterminados:

int int_arr[3];

Si los miembros de la matriz no tienen un constructor predeterminado, el compilador emite un error.

Inicialización predeterminada de variables constantes

Las variables constantes se deben declarar junto con un inicializador. Si son tipos escalares, generan un error del compilador, y si son tipos de clase que tienen un constructor predeterminado, generan una advertencia:

class MyClass{};
int main() {
    //const int i2;   // compiler error C2734: const object must be initialized if not extern
    //const char c2;  // same error
    const MyClass mc1; // compiler error C4269: 'const automatic data initialized with compiler generated default constructor produces unreliable results
}

Inicialización predeterminada de variables estáticas

Las variables estáticas que se declaran sin un inicializador se inicializan en 0 (se convierten implícitamente al tipo):

class MyClass {   
private:
    int m_int;
    char m_char;
};

int main() {
    static int int1;       // 0
    static char char1;     // '\0'
    static bool bool1;   // false
    static MyClass mc1;     // {0, '\0'}
}

Para obtener más información sobre la inicialización de objetos estáticos globales, vea Consideraciones de inicio adicionales.

Inicialización de un valor

La inicialización de un valor se produce en estos casos:

  • Un valor con nombre se inicializa mediante llaves vacías.

  • Un objeto temporal anónimo se inicializa mediante paréntesis o llaves vacíos.

  • Un objeto se inicializa mediante la palabra clave new y paréntesis o llaves vacíos.

La inicialización de un valor hace lo siguiente:

  • Para las clases que tienen al menos un constructor público, se llama al constructor predeterminado.

  • Para las clases que no son de unión y no tienen constructores declarados, el objeto se inicializa en cero y se llama al constructor predeterminado.

  • Para las matrices, se inicializa el valor de cada elemento.

  • En todos los demás casos, la variable se inicializa en cero.

class BaseClass {  
private:
    int m_int;
};

int main() {
    BaseClass bc{};     // class is initialized
    BaseClass*  bc2 = new BaseClass();  // class is initialized, m_int value is 0
    int int_arr[3]{};  // value of all members is 0
    int a{};     // value of a is 0
    double b{};  // value of b is 0.00000000000000000
}

Inicialización de copia

La inicialización de copia es la inicialización de un objeto mediante otro objeto. Se realiza en estos casos:

  • Se inicializa una variable mediante un signo igual.

  • Se pasa un argumento a una función.

  • Se devuelve un objeto de una función.

  • Se produce o detecta una excepción.

  • Se inicializa un miembro de datos no estático con un signo igual.

  • Se inicializan los miembros class, struc y union mediante la inicialización de copia durante la inicialización de agregado. Vea Inicialización de agregado para obtener ejemplos.

En este código se muestran ejemplos de inicialización de copia:

#include <iostream>
using namespace std;

class MyClass{
public:
    MyClass(int myInt) {}
    void set_int(int myInt) { m_int = myInt; }
    int get_int() const { return m_int; }
private:
    int m_int = 7; // copy initialization of m_int

};
class MyException : public exception{};
int main() {
    int i = 5;              // copy initialization of i
    MyClass mc1{ i };
    MyClass mc2 = mc1;      // copy initialization of mc2 from mc1
    MyClass mc1.set_int(i);    // copy initialization of parameter from i
    int i2 = mc2.get_int(); // copy initialization of i2 from return value of get_int()

    try{
        throw MyException();    
    }
    catch (MyException ex){ // copy initialization of ex
        cout << ex.what();  
    }
}

La inicialización de copia no puede invocar constructores explícitos:

vector<int> v = 10; // the constructor is explicit; compiler error C2440: cannot convert from 'int' to 'std::vector<int,std::allocator<_Ty>>'
regex r = "a.*b"; // the constructor is explicit; same error
shared_ptr<int> sp = new int(1729); // the constructor is explicit; same error

En algunos casos, si el constructor de copias de la clase se ha eliminado o es inaccesible, la inicialización de copia produce un error del compilador. Para obtener más información, vea Inicialización explícita.

Inicialización directa

La inicialización directa utiliza llaves o paréntesis (no vacíos). A diferencia de la inicialización de copia, puede invocar constructores explícitos. Se realiza en estos casos:

  • Una variable se inicializa mediante llaves o paréntesis no vacíos.

  • Una variable se inicializa mediante la palabra clave new y llaves o paréntesis no vacíos.

  • Una variable se inicializa mediante static_cast.

  • En un constructor, las clases base y los miembros no estáticos se inicializan mediante una lista de inicializadores.

  • En la copia de una variable capturada en una expresión lambda.

Estos son algunos ejemplos de inicialización directa:

class BaseClass{
public:
    BaseClass(int n) :m_int(n){} // m_int is direct initialized
private:
    int m_int;
};

class DerivedClass : public BaseClass{
public:
    // BaseClass and m_char are direct initialized
    DerivedClass(int n, char c) : BaseClass(n), m_char(c) {}
private:
    char m_char;
};
int main(){
    BaseClass bc1(5);
    DerivedClass dc1{ 1, 'c' };
    BaseClass* bc2 = new BaseClass(7);
    BaseClass bc3 = static_cast<BaseClass>(dc1);

    int a = 1;
    function<int()> func = [a](){  return a + 1; }; // a is direct initialized
    int n = func();
}

Inicialización de lista

La inicialización de lista se produce cuando se inicializa una variable mediante una lista de inicializadores entre llaves. Se pueden utilizar listas de inicializadores entre llaves en estos casos:

  • Se inicializa una variable.

  • Se inicializa una clase mediante la palabra clave new.

  • Se devuelve un objeto de una función.

  • Se pasa un argumento a una función.

  • Uno de los argumentos de una inicialización directa.

  • En un inicializador de miembros de datos no estáticos.

  • En una lista de inicializadores del constructor.

Ejemplos de inicialización de lista:

class MyClass {
public:
    MyClass(int myInt, char myChar) {}  
private:
    int m_int[]{ 3 };
    char m_char;
};
class MyClassConsumer{
public:
    void set_class(MyClass c) {}
    MyClass get_class() { return MyClass{ 0, '\0' }; }
};
struct MyStruct{
    int my_int;
    char my_char;
    MyClass my_class;
};
int main() {
    MyClass mc1{ 1, 'a' };
    MyClass* mc2 = new MyClass{ 2, 'b' };
    MyClass mc3 = { 3, 'c' };

    MyClassConsumer mcc;
    mcc.set_class(MyClass{ 3, 'c' });
    mcc.set_class({ 4, 'd' });

    MyStruct ms1{ 1, 'a', { 2, 'b' } };
}

Inicialización de agregado

La inicialización de agregado es una forma de inicialización de lista para matrices o tipos de clase (a menudo structs o uniones) que tienen:

  • Ningún miembro privado o protegido.

  • Ningún constructor proporcionado por el usuario, salvo para los constructores eliminados o establecidos como valor predeterminado explícitamente.

  • Ninguna clase base.

  • Ninguna función miembro virtual.

  • Ningún inicializador de llave o igualdad para los miembros no estáticos.

Los inicializadores de agregado constan de una lista de inicialización entre llaves, con o sin un signo de igualdad:

#include <iostream>
using namespace std;

struct MyAggregate{
    int myInt;
    char myChar;
};

int main() {
    MyAggregate agg1{ 1, 'c' };

    cout << "agg1: " << agg1.myChar << ": " << agg1.myInt << endl;
    cout << "agg2: " << agg2.myChar << ": " << agg2.myInt << endl;

    int myArr1[]{ 1, 2, 3, 4 };
    int myArr2[3] = { 5, 6, 7 };
    int myArr3[5] = { 8, 9, 10 };

    cout << "myArr1: ";
    for (int i : myArr1){
        cout << i << " ";
    }
    cout << endl;
    
    cout << "myArr3: ";
    for (auto const &i : myArr3) {
        cout << i << " ";
    }
    cout << endl;
}

Este es el resultado:

agg1: c: 1
agg2: d: 2
myArr1: 1 2 3 4
myArr3: 8 9 10 0 0
Nota importanteImportante

Los miembros de la matriz que se declararon pero no se inicializaron explícitamente durante la inicialización de agregado se inicializan en cero, como en myArr3.

Inicializar uniones y structs

Si una unión no tiene un constructor, puede inicializarla mediante un valor (o mediante otra instancia de una unión). El valor se utiliza para inicializar el primer campo no estático. Esto es diferente de la inicialización de struct, donde el primer valor del inicializador se utiliza para inicializar el primer campo, el segundo valor para inicializar el segundo campo, y así sucesivamente. Compare la inicialización de uniones y structs en este ejemplo:

struct MyStruct {
    int myInt;
    char myChar;
};
union MyUnion {
    int my_int;
    char my_char;
    bool my_bool;
    MyStruct my_struct;
};

int main() {  
    MyUnion mu1{ 'a' };  // my_int = 97, my_char = 'a', my_bool = true, {myInt = 97, myChar = '\0'}
    MyUnion mu2{ 1 };   // my_int = 1, my_char = 'x1', my_bool = true, {myInt = 1, myChar = '\0'}
    MyUnion mu3{};      // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
    MyUnion mu4 = mu3;  // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
    //MyUnion mu5{ 1, 'a', true };  // compiler error: C2078: too many initializers
    //MyUnion mu6 = 'a';            // compiler error: C2440: cannot convert from 'char' to 'MyUnion'
    //MyUnion mu7 = 1;              // compiler error: C2440: cannot convert from 'int' to 'MyUnion'

    MyStruct ms1{ 'a' };            // myInt = 97, myChar = '\0'
    MyStruct ms2{ 1 };              // myInt = 1, myChar = '\0'
    MyStruct ms3{};                 // myInt = 0, myChar = '\0'
    MyStruct ms4{1, 'a'};           // myInt = 1, myChar = 'a'
    MyStruct ms5 = { 2, 'b' };      // myInt = 2, myChar = 'b'
}

Inicializar agregados que contienen agregados

Los tipos agregados pueden contener otros tipos agregados, como matrices de matrices, matrices de structs, etc. Estos tipos se inicializan mediante conjuntos anidados de llaves:

struct MyStruct {
    int myInt;
    char myChar;
};
int main() {
    int intArr1[2][2]{{ 1, 2 }, { 3, 4 }};
    int intArr3[2][2] = {1, 2, 3, 4};  
    MyStruct structArr[]{ { 1, 'a' }, { 2, 'b' }, {3, 'c'} };
}

Inicialización de referencia

Para obtener información sobre la inicialización de referencia, vea Inicializar referencias.

Inicialización de variables externas

Las declaraciones de variables automáticas, de registro, estáticas y externas pueden contener inicializadores. Sin embargo, las declaraciones de variables externas solo pueden contener inicializadores si las variables no se declaran como extern. Para obtener más información, vea Externos.

Vea también

Referencia

Declaradores