Стандарты программирования на С++. 101 правило и рекомендация | страница 86
Пример. Использование функции-фабрики для принудительного вызова постконструктора. Рассмотрим следующий код:
>class B { // Корень иерархии
>protected:
> В() {/*...*/ }
> virtual void PostInitialize() // Вызывается сразу
> {/*...*/} // после конструирования
>publiс:
> template
> static shared_ptr
> { // создания объектов
> shared_ptr
> p->PostInitialize();
> return p;
> }
>};
>class D : public B { /* ... */ }; // Некоторый производный
> // класс
>shared_ptr
Этот не вполне надежный дизайн основан на ряде компромиссов.
• Производные классы, такие как >D
, не должны иметь открытых конструкторов. В противном случае пользователи >D
смогут создавать объекты >D
, для которых не будет вызываться функция >PostInitialize
.
• Создание объекта использует оператор >new
, который, однако, может быть перекрыт классом >В
(см. рекомендации 45 и 46).
• Класс >D
обязательно должен определить конструктор с теми же параметрами, что и у конструктора класса >B
. Смягчить проблему может наличие нескольких перегрузок функции >Create
; эти перегрузки могут даже быть шаблонами.
• Если перечисленные требования удовлетворены, данный дизайн гарантирует, что функция >PostInitialize
будет вызвана для любого полностью сконструированного объекта класса, производного от >B
. Функция >PostInitialize
не обязательно должна быть виртуальной; однако она может свободно вызывать виртуальные функции.
[Alexandrescu01] §3 • [Boost] • [Dewhurst03] §75 • [Meyers97] §46 • [Stroustrup00] §15.4.3 • [Taligent94]
50. Делайте деструкторы базовых классов открытыми и виртуальными либо защищенными и невиртуальными
Удалять или не удалять — вот в чем вопрос! Если следует обеспечить возможность удаления посредством указателя на базовый класс, то деструктор базового класса должен быть открытым и виртуальным. В противном случае он должен быть защищенным и невиртуальным.
Это простое правило иллюстрирует достаточно тонкий вопрос и отражает современное использование наследования и принципов объектно-ориентированного проектирования.
Для данного базового класса >Base
вызывающий код может пытаться удалять производные от него объекты посредством указателей на >Base
. Если деструктор >Base
открытый и невиртуальный (свойства по умолчанию), он может быть случайно вызван для указателя, который в действительности указывает на производный объект, и в этом случае мы получим неопределенное поведение. Такое состояние дел привело к тому, что в старых стандартах кодирования требовалось делать деструкторы всех без исключения базовых классов виртуальными. Однако это уже перебор (даже если это и оправдано в общем случае); вместо этого следует использовать правило, согласно которому деструктор базового класса должен быть виртуальным тогда и только тогда, когда он открытый.