Последнее обновление:2026-10-12
Идиома списков инициализации класса в C++¶
- инициализация const-членов возможна только в списке инициализации конструктора
- инициализация членов-ссылок — тоже только в списке инициализации конструктора
- инициализация объектных членов (когда класс соответствующего объектного члена не имеет конструктора по умолчанию) — тоже только в списке инициализации конструктора
Вложенность классов¶
Первый способ: использовать список инициализации.
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;
}
Результат работы:
Animal() is called
Animal (const Animal &) is called
Dog(const Animal &animal) is called
~Dog() is called
~Animal() is called
~Animal() is called
Проанализируем по порядку сверху вниз:
В функции main строка Animal animal; вызывает конструктор по умолчанию.
Dog d(animal); эквивалентно:
В конце вызываются деструкторы, и программа завершается.
Второй способ: инициализация объекта присваиванием в конструкторе.
Изменим конструктор следующим образом:
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
Отсюда вывод:
Вызов Dog d(animal); эквивалентен:
сначала создаётся объект, а затем выполняется присваивание, поэтому сначала вызывается конструктор по умолчанию, а затем перегруженный оператор =.
Краткие итоги
Из рассмотренного выше мы делаем следующие выводы:
- если класс содержит другие пользовательские class или struct, то при использовании списка инициализации объект фактически создаётся и инициализируется одновременно
- а при использовании присваивания внутри класса это эквивалентно сначала определению объекта, а затем присваиванию: обычно сначала вызывается конструктор по умолчанию, а затем перегруженный оператор =.
Наследование при отсутствии конструктора по умолчанию¶
Теперь рассмотрим замену описанных выше отношений на наследование и изменим конструкторы Animal и Dog, как в коде ниже:
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;
}
};
Выше параметры передаются параметризованному конструктору базового класса через список инициализации. А что произойдёт, если не передавать их через список инициализации?
Уберём список инициализации
Запустим программу:Поскольку в Animal нет конструктора по умолчанию, возникает ошибка — такая проблема катастрофична, и её следует по возможности избегать, инициализируя конструктор базового класса через список инициализации.
const-члены и члены-ссылки в классе¶
Особенно члены-ссылки: их необходимо инициализировать в списке инициализации, а не присваиванием!
Например: добавим в рассмотренный выше Animal закрытые члены и изменим конструктор:
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_;
};
Получим следующую ошибку:
Следует изменить так:
Animal(int age, std::string name) : age_(age), name_(name) {
std::cout << "Animal(int age) is called" << std::endl;
}
Похожие темы¶
- Отказ от конструктора по умолчанию в C++ | C++那些事
- C++: классы-шаблоны вместо шаблонов функций | C++那些事
- Идиома copy-and-swap в C++ | C++那些事
- Внедрение зависимостей в C++ | C++那些事
- Сильнотипизированные перечисления enum class в C++ | C++那些事
- Header-Only в C++ против разделения реализации | C++那些事
- Идиома ленивых вычислений в C++ | C++那些事
- Идиома таблиц поиска в C++ | C++那些事
- Указатели на члены и информация о типах в C++ | C++那些事
- Идиома Mixin в C++ | C++那些事