Если конструктор делегирует создание объекта другому конструктору того же класса, какой конструктор отвечает за инициализацию членов и что произойдёт при попытке дополнительно указать инициализаторы?
При делегировании полный объект и все его члены инициализирует целевой конструктор. Делегирующий конструктор может иметь в списке инициализации только вызов другого конструктора этого же класса; одновременно указывать базовые классы или отдельные члены нельзя. После завершения целевого конструктора выполняется тело делегирующего конструктора.
Делегирующие конструкторы появились в C++11, чтобы несколько конструкторов могли повторно использовать единую логику инициализации. До этого одинаковые списки инициализации приходилось дублировать либо выносить подготовку данных в отдельные функции, что повышало риск расхождения поведения.
Подход решает именно проблему единственного источника инициализации объекта: один конструктор становится ответственным за корректное создание состояния, а остальные задают удобные варианты входных параметров.
Если разные конструкторы самостоятельно инициализируют одни и те же члены, со временем они могут начать устанавливать разные значения, нарушать инварианты или по-разному обрабатывать ошибки. Делегирование устраняет дублирование, но требует понимать, кто именно создаёт объект и в каком порядке выполняются тела конструкторов.
Неверное ожидание состоит в том, что делегирующий конструктор сначала инициализирует собственные поля, а затем вызывает другой конструктор. В C++ происходит обратное: сначала полностью выполняется целевой конструктор, и только после этого — тело делегирующего.
Синтаксически делегирующий конструктор указывает в списке инициализации имя текущего класса. Такой вызов должен быть единственным элементом списка инициализации. Поэтому нельзя одновременно делегировать и отдельно задавать значения членов или базовых классов.
В примере конструктор без параметров не инициализирует name и access самостоятельно. Это делает целевой конструктор с двумя параметрами. Только после его завершения могло бы выполняться тело конструктора без параметров.
Целевой конструктор инициализирует базовые классы и нестатические члены полного объекта. Они не инициализируются повторно делегирующим конструктором. Если целевой конструктор выбрасывает исключение, тело делегирующего конструктора не выполняется.
Цепочка делегирования допустима, если она в конечном счёте приводит к конструктору, который непосредственно инициализирует объект. Циклическое делегирование, при котором конструкторы прямо или косвенно вызывают друг друга по кругу, некорректно и диагностируется компилятором.
Делегирование не является вызовом обычной функции после создания объекта. Это часть процесса конструирования, поэтому виртуальные базовые классы, обычные базовые классы и члены создаются только один раз — целевым конструктором.
В классе конфигурации есть конструкторы для значений по умолчанию, файла и готового набора параметров. Вариант с копированием списков инициализации прост для начала, но постепенно приводит к расхождению проверок и значений по умолчанию. Вариант с отдельной приватной функцией лучше разделяет вычисления, но не заменяет инициализацию базовых класов и членов и может оставить объект частично настроенным.
Выбранное решение — сделать один конструктор основным, а остальные направить через делегирование к нему. Тогда проверка диапазонов, установка значений и поддержание инвариантов находятся в одном месте. Результат — меньше дублирования и одинаковое состояние объекта независимо от выбранного способа создания.
Вопрос: Выполняется ли тело делегирующего конструктора до тела целевого?
Ответ: Нет. Сначала выполняется целевой конструктор: он инициализирует базовые классы и члены, затем выполняет своё тело. После его успешного завершения выполняется тело делегирующего конструктора. Если целевой конструктор завершился исключением, делегирующее тело не запускается.
Вопрос: Можно ли в делегирующем конструкторе дополнительно инициализировать один из членов класса?
Ответ: Нельзя. Делегирующий вызов должен быть единственным мем-инициализатором. Попытка добавить рядом инициализацию члена или базового класса делает объявление некорректным. Член нужно инициализировать в целевом конструкторе либо изменить архитектуру конструкторов.
Вопрос: Инициализируются ли виртуальные базовые классы повторно при делегировании?
Ответ: Нет. Делегирование относится к созданию одного полного объекта, а не к созданию временного промежуточного объекта. Виртуальные базовые классы, обычные базовые классы и члены инициализируются один раз в рамках целевого конструктора, после чего выполняются тела конструкторов в порядке, установленном правилами C++.