Menu
Coddy logo textTech

Списки инициализации конструктора

Часть раздела Объектно-ориентированное программирование путешествия по 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; }  // Ошибка!
};

Члены класса инициализируются в том порядке, в котором они объявлены в классе, а не в том порядке, в котором они указаны в списке инициализации. Всегда пишите список инициализации в порядке объявления, чтобы избежать путаницы.

challenge icon

Задание

Легко

Давайте создадим систему конфигурации для игрового движка, которая продемонстрирует, когда и почему списки инициализации членов (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;
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование