Списки инициализации конструктора
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy — урок 22 из 104.
Вы уже видели синтаксис с двоеточием в предыдущих уроках, например: Player(std::string n, int h) : name(n), health(h) {}. Это называется списком инициализации членов, и это предпочтительный способ инициализации членов в C++.
Список инициализации следует за списком параметров конструктора, начинаясь с двоеточия. Каждый член инициализируется непосредственно своим значением; элементы списка разделяются запятыми:
class Rectangle {
int width;
int height;
const int id;
int& reference;
public:
Rectangle(int w, int h, int i, int& r)
: width(w), height(h), id(i), reference(r) {
// Тело конструктора (может быть пустым)
}
};Почему следует использовать списки инициализации вместо присваивания в теле конструктора? При использовании присваивания члены сначала конструируются по умолчанию, а затем им присваиваются новые значения. Со списками инициализации члены конструируются сразу с нужными значениями — это эффективнее, а в некоторых случаях необходимо.
Определенные члены должны быть инициализированы с помощью списка:
- члены
const— не могут быть переназначены после создания
- Члены-ссылки — должны быть привязаны при инициализации
- Члены без конструкторов по умолчанию
class Player {
const int maxHealth; // Необходимо использовать список инициализации
std::string name;
public:
// Это работает
Player(int max, std::string n) : maxHealth(max), name(n) {}
// Это НЕ скомпилируется:
// Player(int max) { maxHealth = max; } // Ошибка!
};Члены класса инициализируются в том порядке, в котором они объявлены в классе, а не в том порядке, в котором они указаны в списке инициализации. Всегда пишите список инициализации в порядке объявления, чтобы избежать путаницы.
Задание
ЛегкоДавайте создадим систему конфигурации для игрового движка, которая продемонстрирует, когда и почему списки инициализации членов (member initializer lists) необходимы. Вы создадите класс GameConfig с членами, которые должны быть инициализированы с использованием синтаксиса списка инициализации.
Для организации кода вы создадите два файла:
GameConfig.h: Определите классGameConfig, который хранит настройки игры. Ваш класс должен содержать:const std::string gameName— название игры, которое не может быть изменено после созданияconst int maxPlayers— максимальное количество игроков, фиксированное при созданииint& difficultyRef— ссылка на внешнюю настройку сложностиint screenWidthиint screenHeight— обычные члены для настроек экрана- Конструктор, который принимает все необходимые параметры и инициализирует каждый член, используя список инициализации. Список инициализации должен соответствовать порядку объявления членов в классе
- Метод
display(), который выводит конфигурацию в следующем формате:Game: <gameName> Max Players: <maxPlayers> Difficulty: <difficultyRef value> Resolution: <screenWidth>x<screenHeight>
main.cpp: Считайте значения конфигурации из входных данных и создайте объект GameConfig. Вам потребуется:- Считать название игры (string), максимальное количество игроков (int), сложность (int), ширину (int) и высоту (int) из входных данных — каждое значение на отдельной строке
- Создать локальную переменную типа
intдля сложности, на которую будет ссылаться конфиг - Создать объект
GameConfig, используя конструктор со списком инициализации - Вызвать
display()для отображения начальной конфигурации - Изменить переменную сложности напрямую (умножить её на 2)
- Вывести
"After difficulty change:" - Снова вызвать
display(), чтобы показать, что ссылка отражает обновленное значение
Эта задача подчеркивает, почему списки инициализации важны: ваши const члены и член-ссылка не могут быть присвоены в теле конструктора — они должны быть инициализированы в списке. Член-ссылка также демонстрирует, как изменения в исходной переменной отражаются через объект конфигурации.
Подключите ваш заголовочный файл в main.cpp с помощью #include "GameConfig.h".
Шпаргалка
Список инициализации членов — это предпочтительный способ инициализации членов класса в C++. Он указывается после списка параметров конструктора, начинается с двоеточия, и каждый член инициализируется напрямую:
class Rectangle {
int width;
int height;
public:
Rectangle(int w, int h) : width(w), height(h) {
// Тело конструктора
}
};Зачем использовать списки инициализации? Члены создаются сразу с правильными значениями, а не создаются по умолчанию с последующим присваиванием, что делает процесс более эффективным.
Обязательно для определенных членов:
constчлены — не могут быть изменены после создания- Члены-ссылки — должны быть привязаны при инициализации
- Члены без конструкторов по умолчанию
class Player {
const int maxHealth;
int& reference;
public:
// Правильно — используется список инициализации
Player(int max, int& r) : maxHealth(max), reference(r) {}
// Ошибка — нельзя присвоить значение const или ссылке в теле
// Player(int max) { maxHealth = max; }
};Важно: Члены инициализируются в том порядке, в котором они объявлены в классе, а не в том порядке, в котором они указаны в списке инициализации. Всегда пишите список инициализации в порядке объявления.
Попробуйте сами
#include <iostream>
#include <string>
#include "GameConfig.h"
using namespace std;
int main() {
// Считать входные значения
string gameName;
getline(cin, gameName);
int maxPlayers;
cin >> maxPlayers;
int difficulty;
cin >> difficulty;
int width;
cin >> width;
int height;
cin >> height;
// TODO: Создать объект GameConfig, используя конструктор со списком инициализации
// Конструктор должен принимать: gameName, maxPlayers, difficulty (по ссылке), width, height
// TODO: Вызвать display(), чтобы показать начальную конфигурацию
// TODO: Изменить переменную difficulty напрямую (умножить её на 2)
// TODO: Вывести "After difficulty change:"
// TODO: Снова вызвать display(), чтобы показать обновленную конфигурацию
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыСборка и компиляция C++Заголовочные файлы и файлы исходного кодаПространства имен и область видимостиВведение в ООП на C++Классы и объектыУказатель 'this'Методы (функции-члены)Атрибуты (члены данных)Основы конструкторов и деструкторовИтоги — Простой калькулятор4Свойства классов
Члены экземпляра против статическихГеттеры и сеттерыКонстантные функции-членыКлючевое слово mutableСтатические методы и переменныеДружественные функции и классыИтоги — Менеджер банковских счетов7Наследование
Основы наследованияУровни доступа при наследованииПорядок вызова конструкторов и деструкторовПереопределение методовВиртуальные функции и VTableМножественное наследованиеВиртуальное наследованиеИтоги — Иерархия сотрудников10Обзор STL
Обзор и философия STLКонтейнеры STLИтераторыАлгоритмы STLФункторы и лямбда-выраженияИтоги: частота слов13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonFactory и Abstract FactoryПаттерн BuilderПаттерн ObserverПаттерн Strategy2Управление памятью
Стек и кучаУказатели и ссылкиДинамическая память (new/delete)Умные указатели в C++RAII в C++Итоги — Менеджер динамического массива5Инкапсуляция
Спецификаторы доступа в C++Спецификаторы доступа: подробный разборСокрытие данныхStruct против ClassВложенные и внутренние классыИтоги — Система учета студентов8Полиморфизм
Полиморфизм компиляции и времени выполненияПерегрузка функцийВиртуальные функции: повторениеЧистые виртуальные функцииАбстрактные классыПроектирование интерфейсов на C++Dynamic Casting и RTTIИтоги: Калькулятор фигур11Продвинутые концепции ООП
Композиция против наследованияМиксины через CRTPИдиома PimplСтирание типовEnum Classes и строгая типизацияОбработка исключений в ООППользовательские иерархии исключений14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикRAII как паттерн3Конструкторы и деструкторы
Конструктор по умолчаниюПараметризованный конструкторКонструктор копированияКонструктор перемещенияСписки инициализации конструктораДелегирующие конструкторыГлубокое погружение в деструкторыПравило трех / пяти / нуляПовторение — класс String6Перегрузка операторов
Введение в перегрузку операторовПерегрузка арифметических операторовПерегрузка операторов сравненияОператоры потоковПерегрузка оператора присваиванияПерегрузка операторов [] и ()Операторы преобразования типовИтоги — класс Matrix9Шаблоны
Шаблоны функцийШаблоны классовСпециализация шаблоновВариативные шаблоныОсновы SFINAE и Type TraitsИтоги — Обобщенный контейнер