Compartilhar via


Inicializadores

Um inicializador especifica o valor inicial de uma variável. Você pode inicializar variáveis nestes contextos:

  • Na definição de uma variável:

    int i = 3;
    Point p1{ 1, 2 };
    
  • Como um dos parâmetros de uma função:

    set_point(Point{ 5, 6 });
    
  • Como o valor de retorno de uma função:

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

Os inicializadores podem usar estes formatos:

  • Uma expressão (ou uma lista de expressões separadas por vírgulas) entre parênteses:

    Point p1(1, 2);
    
  • Um sinal de igual seguido por uma expressão:

    string s = "hello";
    
  • Uma lista de inicializadores entre chaves. A lista pode estar vazia ou pode consistir em um 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 } });
    }
    

Tipos de inicialização

Há vários tipos de inicialização que podem ocorrer em pontos diferentes na execução do programa. Os tipos diferentes de inicialização não são mutuamente exclusivos — por exemplo, a inicialização de lista pode acionar a inicialização de valor e, em outras circunstâncias, pode acionar a inicialização de agregação.

Inicialização do zero

A inicialização do zero é a configuração de uma variável para um valor zero convertido implicitamente no tipo:

  • As variáveis numéricas são inicializadas como 0 (ou 0,0 ou 0,0000000000, etc.).

  • As variáveis char são inicializadas como '\0'.

  • Os ponteiros são inicializados como nullptr.

  • Matrizes, classes POD, estruturas e uniões têm seus membros inicializados como um valor zero.

A inicialização do zero é executada em diferentes momentos:

  • Na inicialização do programa, para todas as variáveis nomeadas que têm a duração estática. Essas variáveis podem ser inicializadas novamente mais tarde.

  • Durante a inicialização de valor, para tipos escalares e tipos de classe POD que são inicializados usando chaves vazias.

  • Para matrizes que têm apenas um subconjunto de seus membros inicializado.

Veja alguns exemplos de inicialização do zero:

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'
}

Inicialização padrão

A inicialização padrão para classes, estruturas e uniões é a inicialização que usa um construtor padrão. O construtor padrão pode ser chamado por não incluir uma expressão de inicialização ou por usar a palavra-chave new:

MyClass mc1;
MyClass* mc3 = new MyClass;

Se a classe, estrutura ou união não tiver um construtor diferente, o compilador emitirá um erro.

As variáveis escalares são inicializadas por padrão quando são definidas sem uma expressão de inicialização. Elas têm valores indeterminados.

int i1;
float f;
char c;

As matrizes são inicializadas por padrão quando são definidas sem uma expressão de inicialização. Quando uma matriz é inicializada por padrão, seus membros são inicializados por padrão e têm valores indeterminados:

int int_arr[3];

Se os membros da matriz não tiverem um construtor padrão, o compilador emitirá um erro.

Inicialização padrão de variáveis constantes

As variáveis constantes devem ser declaradas juntamente com um inicializador. Se forem de tipos escalares, elas provocam um erro de compilador e, se forem de tipos de classe que têm um construtor padrão, elas geram um aviso:

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
}

Inicialização padrão de variáveis estáticas

As variáveis estáticas que são declaradas sem um inicializador são inicializadas como 0 (implicitamente convertidas no 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 obter mais informações sobre inicialização de objetos estáticos globais, consulte Considerações de inicialização adicionais.

Inicialização de valor

A inicialização de valor ocorre nestes casos:

  • Um valor nomeado é inicializado usando a inicialização de chaves vazias.

  • Um objeto temporário anônimo é inicializado usando parênteses ou chaves vazios.

  • Um objeto é inicializado usando a palavra-chave new mais parênteses ou chaves vazios.

Veja a ação da inicialização de valor:

  • Para classes que têm pelo menos um construtor público, o construtor padrão é chamado.

  • Para classes de não união que não têm construtores declarados, o objeto é inicializado do zero e o construtor padrão é chamado.

  • Para matrizes, cada elemento é inicializado do zero.

  • Em todos os outros casos, a variável é inicializada do zero.

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
}

Inicialização de cópia

A inicialização de cópia é a inicialização de um objeto usando um objeto diferente. Ela ocorre nestes casos:

  • Uma variável é inicializada usando um sinal de igual.

  • Um argumento é transmitido para uma função.

  • Um objeto é retornado de uma função.

  • Uma exceção é lançada ou capturada.

  • Um membro de dados não estático é inicializado usando um sinal de igual.

  • Membros da união, classe e estrutura são inicializados pela inicialização de cópia durante a inicialização de agregação. Consulte Inicialização de agregação para ver exemplos.

Este código mostra exemplos da inicialização de cópia:

#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();  
    }
}

A inicialização de cópia não pode invocar construtores 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

Em alguns casos, se o construtor de cópia da classe for excluído ou estiver inacessível, a inicialização de cópia emitirá um erro de compilador. Para obter mais informações, consulte Inicialização explícita.

Inicialização direta

A inicialização direta usa chaves ou parênteses (não vazios). Diferentemente da inicialização de cópia, ela pode invocar construtores explícitos. Ela ocorre nestes casos:

  • Uma variável é inicializada usando chaves ou parênteses não vazios.

  • Uma variável inicializada usando a palavra-chave new mais parênteses ou chaves não vazios.

  • Uma variável é inicializada usando static_cast.

  • Em um construtor, as classes base e os membros não estáticos são inicializados usando uma lista de inicializadores.

  • Na cópia de uma variável capturada em uma expressão lambda.

Veja exemplos de inicialização direta:

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();
}

Inicialização de lista

A inicialização de lista ocorre quando uma variável é inicializada usando uma lista de inicializadores entre chaves. As listas de inicializadores entre chaves podem ser usadas nestes casos:

  • Uma variável é inicializada.

  • Uma classe é inicializada usando a palavra-chave new.

  • Um objeto é retornado de uma função.

  • Um argumento é transmitido para uma função.

  • Um dos argumentos em uma inicialização direta.

  • Em um inicializador de membro de dados não estáticos

  • Em uma lista de inicializadores de construtor.

Exemplos de inicialização 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' } };
}

Inicialização de agregação

A inicialização de agregação é uma forma da inicialização de lista para tipos de classe ou matrizes (muitas vezes, uniões ou estruturas) que não tenham:

  • Nenhum membro particular ou protegido.

  • Nenhum construtor fornecido pelo usuário, exceto construtores explicitamente padronizados ou excluídos.

  • Nenhuma classe base.

  • Nenhuma função membro virtual.

  • Nenhum inicializador igual ou entre chaves para membros não estáticos.

Os inicializadores de agregação consistem em uma lista de inicialização entre chaves, com ou sem um sinal de igual:

#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 é o resultado:

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

Importante

Os membros de matriz que foram declarados, mas não explicitamente inicializados durante a inicialização de agregação são inicializados do zero, como em myArr3.

Inicializando uniões e estruturas

Se uma união não tiver um construtor, você poderá inicializá-la usando um valor (ou usando outra instância de uma união). O valor é usado para inicializar o primeiro campo não estático. Essa inicialização é diferente da inicialização de estrutura, na qual o primeiro valor no inicializador é usado para inicializar o primeiro campo, o segundo valor para inicializar o segundo campo e assim por diante. Compare a inicialização de uniões e estruturas neste exemplo:

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'
}

Inicializando agregações que contêm agregações

Os tipos de agregação podem conter outros tipos de agregação; por exemplo, matrizes de matrizes, matrizes de estruturas, etc. Esses tipos são inicializados usando conjuntos de chaves aninhados:

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'} };
}

Inicialização de referência

Para obter informações sobre inicialização de referência, consulte Inicializando referências.

Inicialização de variáveis externas

As declarações de variáveis automáticas, de registro, estáticas e externas podem conter inicializadores. No entanto, as declarações de variáveis externas poderão conter inicializadores apenas se as variáveis não forem declaradas como extern. Para obter mais informações, consulte Externo.

Consulte também

Referência

Declaradores