Última atualização:2026-10-12
Idiom de lista de inicialização de classes em C++¶
- A inicialização de membros const só pode ser feita na lista de inicialização do construtor
- A inicialização de membros de referência também só pode ser feita na lista de inicialização do construtor
- A inicialização de membros-objeto (cuja classe correspondente não tem construtor padrão) também só pode ser feita na lista de inicialização do construtor
Aninhamento entre classes¶
Primeira forma: usar a lista de inicialização.
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 da execução:
Animal() is called
Animal (const Animal &) is called
Dog(const Animal &animal) is called
~Dog() is called
~Animal() is called
~Animal() is called
Analisando de cima para baixo, em ordem:
No main, Animal animal; chama o construtor padrão.
Dog d(animal); equivale a:
Por fim, o destrutor é chamado e o programa termina.
Segunda forma: inicializar o objeto por atribuição no construtor.
O construtor é modificado assim:
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
Conclui-se então:
Ao chamar Dog d(animal);, equivale a:
definir primeiro o objeto e só depois atribuir; por isso, chama-se primeiro o construtor padrão e, em seguida, a função de sobrecarga do operador = .
Resumo
Com o exposto acima, chegamos às seguintes conclusões:
- Quando a classe contém outra class ou struct personalizada, usar a lista de inicialização significa, na prática, criar o objeto e inicializá-lo ao mesmo tempo
- Já a atribuição dentro da classe equivale a definir primeiro o objeto e atribuir depois: normalmente chama-se primeiro o construtor padrão e, então, a função de sobrecarga do operador =.
Relação de herança sem construtor padrão¶
Agora considere mudar a relação acima para herança e modificar os construtores de Animal e Dog, conforme o código abaixo:
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;
}
};
O acima passa parâmetros ao construtor parametrizado da classe base por meio da lista de inicialização; se não os passarmos pela lista de inicialização, que impacto haverá?
Removendo a lista de inicialização
Executando o programa:Como Animal não tem construtor padrão, dá erro; deparar-se com esse tipo de problema é catastrófico e devemos evitá-lo ao máximo: pode-se inicializar o construtor da classe base por meio da lista de inicialização.
Membros de dados const e membros de dados de referência na classe¶
Em particular, os membros de dados de referência devem ser inicializados com a lista de inicialização, e não por atribuição!
Por exemplo: adicionar membros privados ao Animal acima e modificar o construtor:
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_;
};
Reporta o erro abaixo:
Deveria ser alterado para:
Animal(int age, std::string name) : age_(age), name_(name) {
std::cout << "Animal(int age) is called" << std::endl;
}
Tópicos relacionados¶
- C++ evitar o construtor padrão | C++那些事
- C++: templates de classe em vez de templates de função | C++那些事
- Idiom copy-and-swap em C++ | C++那些事
- Injeção de dependência em C++ | C++那些事
- C++ enum class enumerações fortemente tipadas | C++那些事
- C++ Header-Only versus separação de implementação | C++那些事
- Idiom de avaliação preguiçosa em C++ | C++那些事
- Idiom de consulta em tabela em C++ | C++那些事
- Ponteiros para membros e type info em C++ | C++那些事
- Idiom Mixin em C++ | C++那些事