Стандарты программирования на С++. 101 правило и рекомендация
Шрифт:
[Dewhurst03] §51, §59 • [Keffer95] pp.13-14 • [Meyers97] §12 • [Murray93] §2.1.31 • [Sutter00] §8, §47 • [Sutter02] §18
49. Избегайте вызовов виртуальных функций в конструкторах и деструкторах
Внутри конструкторов и деструкторов виртуальные функции теряют виртуальность. Хуже того — все прямые или косвенные вызовы нереализованных чисто виртуальных функций из конструктора или деструктора приводят к неопределенному поведению. Если ваш дизайн требует виртуальной передачи в производный класс из конструктора или деструктора базового класса, следует воспользоваться иной методикой,
В C++ полный объект конструируется по одному базовому классу за раз.
Пусть у нас есть базовый класс
Кроме того, следует помнить, что вызов из конструктора чисто виртуальной функции, не имеющей определения, приводит к неопределенному поведению.
С другой стороны, в некоторых случаях дизайн требует использования "постконструктора", т.е. виртуальной функции, которая должна быть вызвана после того, как полный объект оказывается сконструирован. Некоторые методики, применяемые для решения этой задачи, описаны в приводимых ниже ссылках. Вот (далеко не исчерпывающий) список возможных решений.
• Перекладывание ответственности. Можно просто документировать необходимость вызова в пользовательском коде постконструктора после создания объекта.
• Отложенная постинициализация. Можно выполнять постинициализацию при первом вызове функции-члена, для чего использовать в базовом классе флаг типа
• Использование семантики базового класса. Правила языка требуют, чтобы конструктор наиболее производного класса определял, какой из конструкторов базовых классов будет вызван. Вы можете использовать это правило в своих целях (см. [Taligent94]).
• Использование функции-фабрики. Таким способом вы можете легко обеспечить принудительный вызов функции-постконструктора (см. примеры к данной рекомендации).
Ни одна из методик постконструирования не является идеальной. Наихудшее, что можно сделать, — это потребовать, чтобы пользователь класса вручную вызывал постконструктор. Однако даже наилучшие способы решения данной задачи требуют отличного от обычного синтаксиса конструирования объекта (что легко проверяется в процессе компиляции) и/или сотрудничества с авторами производных классов (что невозможно проверить в процессе компиляции).
Пример. Использование функции-фабрики для принудительного вызова постконструктора. Рассмотрим следующий код:
Этот не вполне надежный дизайн основан на ряде компромиссов.
• Производные классы, такие как
• Создание объекта использует оператор
• Класс
• Если перечисленные требования удовлетворены, данный дизайн гарантирует, что функция
[Alexandrescu01] §3 • [Boost] • [Dewhurst03] §75 • [Meyers97] §46 • [Stroustrup00] §15.4.3 • [Taligent94]
50. Делайте деструкторы базовых классов открытыми и виртуальными либо защищенными и невиртуальными
Удалять или не удалять — вот в чем вопрос! Если следует обеспечить возможность удаления посредством указателя на базовый класс, то деструктор базового класса должен быть открытым и виртуальным. В противном случае он должен быть защищенным и невиртуальным.
Это простое правило иллюстрирует достаточно тонкий вопрос и отражает современное использование наследования и принципов объектно-ориентированного проектирования.
Для данного базового класса