Делегирующие конструкторы
Часть раздела Объектно-ориентированное программирование путешествия по C++ на Coddy — урок 23 из 104.
Когда у вас есть несколько конструкторов, имеющих общую логику инициализации, вы можете использовать делегирующие конструкторы, чтобы избежать дублирования кода. Делегирующий конструктор вызывает другой конструктор того же класса в своем списке инициализации.
class Player {
std::string name;
int health;
int level;
public:
// Основной конструктор - выполняет фактическую работу
Player(std::string n, int h, int lvl)
: name(n), health(h), level(lvl) {}
// Делегирует основному конструктору
Player(std::string n) : Player(n, 100, 1) {}
// Конструктор по умолчанию тоже делегирует
Player() : Player("Unknown") {}
};Делегирование происходит в списке инициализации с использованием имени класса, за которым следуют аргументы. Когда вызывается Player(), он делегирует выполнение Player(std::string), который, в свою очередь, делегирует выполнение конструктору с тремя параметрами. Цепочка выполняется от целевого конструктора обратно к исходному.
Есть одно важное правило: делегирующий конструктор не может иметь других членов в своем списке инициализации. Делегирование должно быть единственным элементом:
class Item {
int id;
int value;
public:
Item(int i, int v) : id(i), value(v) {}
// Ошибка! Нельзя смешивать делегирование с инициализацией членов
// Item(int i) : Item(i, 0), id(i) {}
// Правильно — только делегирование
Item(int i) : Item(i, 0) {}
};Делегирующие конструкторы помогают централизовать логику инициализации в одном месте, что упрощает поддержку кода и снижает вероятность ошибок, связанных с дублированием кода.
Задание
ЛегкоДавайте создадим систему настроек соединения, которая демонстрирует, как делегирующие конструкторы могут устранить дублирование кода путем связывания вызовов конструкторов в цепочку.
Вы создадите два файла для организации вашего кода:
Connection.h: Определите классConnection, который представляет сетевое соединение с различными параметрами конфигурации. Ваш класс должен иметь:- Приватные члены:
host(string),port(int),timeout(int в секундах) иsecure(bool) - Основной конструктор, который принимает все четыре параметра и инициализирует все члены с помощью списка инициализации. Этот конструктор должен выводить
"Connection configured: <host>:<port>" - Конструктор, принимающий только
hostиport, который делегирует выполнение основному конструктору, при этомtimeoutпо умолчанию равен30, аsecureпо умолчанию равенfalse - Конструктор, принимающий только
host, который делегирует выполнение конструктору с двумя параметрами, при этомportпо умолчанию равен80 - Конструктор по умолчанию, который делегирует выполнение конструктору с одним параметром, при этом
hostпо умолчанию равен"localhost" - Метод
getInfo(), который возвращает строку в формате:"<host>:<port> (timeout: <timeout>s, secure: <yes/no>)"
- Приватные члены:
main.cpp: Продемонстрируйте цепочку делегирования, создав соединения с использованием различных конструкторов. Считайте имя хоста и номер порта из входных данных (каждое на отдельной строке), затем:- Создайте объект
Connection, используя конструктор по умолчанию - Создайте объект
Connection, используя только хост из входных данных - Создайте объект
Connection, используя и хост, и порт из входных данных - Создайте объект
Connectionс входным хостом, входным портом, таймаутом60и параметром secure, установленным вtrue - Выведите результат
getInfo()для каждого соединения, по одному на строку
- Создайте объект
Цепочка делегирования работает следующим образом: конструктор по умолчанию вызывает конструктор с одним параметром, который вызывает конструктор с двумя параметрами, который, в свою очередь, вызывает основной конструктор с четырьмя параметрами. Это означает, что сообщение "Connection configured" выводится один раз для каждого созданного объекта, всегда из основного конструктора.
Для статуса безопасности в getInfo() выводите "yes", если secure равно true, в противном случае — "no".
Подключите ваш заголовочный файл в main.cpp с помощью #include "Connection.h".
Шпаргалка
Делегирующий конструктор вызывает другой конструктор того же класса в своем списке инициализации, чтобы избежать дублирования кода:
class Player {
std::string name;
int health;
int level;
public:
// Основной конструктор
Player(std::string n, int h, int lvl)
: name(n), health(h), level(lvl) {}
// Делегирует основному конструктору
Player(std::string n) : Player(n, 100, 1) {}
// Конструктор по умолчанию тоже делегирует
Player() : Player("Unknown") {}
};Делегирование происходит в списке инициализации с использованием имени класса, за которым следуют аргументы. Конструкторы могут образовывать цепочку делегирования, где один делегирует другому.
Важное правило: Делегирующий конструктор не может содержать другие члены в своем списке инициализации. Делегирование должно быть единственным элементом:
class Item {
int id;
int value;
public:
Item(int i, int v) : id(i), value(v) {}
// Ошибка! Нельзя смешивать делегирование с инициализацией членов
// Item(int i) : Item(i, 0), id(i) {}
// Правильно — только делегирование
Item(int i) : Item(i, 0) {}
};Попробуйте сами
#include <iostream>
#include <string>
#include "Connection.h"
using namespace std;
int main() {
// Чтение входных данных
string host;
int port;
cin >> host;
cin >> port;
// TODO: Создать Connection, используя конструктор по умолчанию
// TODO: Создать Connection, используя только host из входных данных
// TODO: Создать Connection, используя и host, и port из входных данных
// TODO: Создать Connection с входными host, port, timeout=60, secure=true
// TODO: Вывести getInfo() для каждого соединения, по одному на строку
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Итоги — Обобщенный контейнер