Стандарты программирования на С++. 101 правило и рекомендация | страница 85
В C++ полный объект конструируется по одному базовому классу за раз.
Пусть у нас есть базовый класс >В
и класс >D
, производный от >B
. При создании объекта >D
, когда выполняется конструктор >В
, динамическим типом создаваемого объекта является >B
. В частности, вызов виртуальной функции >B::Fun
приведет к выполнению функции >Fun
, определенной в классе >В
, независимо от того, перекрывает ее класс >D
или нет. И это хорошо, поскольку вызов функции-члена >D
в тот момент, когда члены объекта >D
еще не инициализированы, может привести к хаосу. Только после завершения выполнения конструктора >В
выполняется тело конструктора >D
и объект приобретает тип >D
. В качестве эмпирического правила следует помнить, что в процессе конструирования >В
нет никакого способа определить, является ли >В
отдельным объектом или базовой частью некоторого иного производного объекта.
Кроме того, следует помнить, что вызов из конструктора чисто виртуальной функции, не имеющей определения, приводит к неопределенному поведению.
С другой стороны, в некоторых случаях дизайн требует использования "постконструктора", т.е. виртуальной функции, которая должна быть вызвана после того, как полный объект оказывается сконструирован. Некоторые методики, применяемые для решения этой задачи, описаны в приводимых ниже ссылках. Вот (далеко не исчерпывающий) список возможных решений.
• Перекладывание ответственности. Можно просто документировать необходимость вызова в пользовательском коде постконструктора после создания объекта.
• Отложенная постинициализация. Можно выполнять постинициализацию при первом вызове функции-члена, для чего использовать в базовом классе флаг типа >bool
, который показывает, был уже вызван постконструктор или нет.
• Использование семантики базового класса. Правила языка требуют, чтобы конструктор наиболее производного класса определял, какой из конструкторов базовых классов будет вызван. Вы можете использовать это правило в своих целях (см. [Taligent94]).
• Использование функции-фабрики. Таким способом вы можете легко обеспечить принудительный вызов функции-постконструктора (см. примеры к данной рекомендации).
Ни одна из методик постконструирования не является идеальной. Наихудшее, что можно сделать, — это потребовать, чтобы пользователь класса вручную вызывал постконструктор. Однако даже наилучшие способы решения данной задачи требуют отличного от обычного синтаксиса конструирования объекта (что легко проверяется в процессе компиляции) и/или сотрудничества с авторами производных классов (что невозможно проверить в процессе компиляции).