Стандарты программирования на С++. 101 правило и рекомендация | страница 86



Примеры

Пример. Использование функции-фабрики для принудительного вызова постконструктора. Рассмотрим следующий код:

>class B {                      // Корень иерархии

>protected:

> В() {/*...*/ }

> virtual void PostInitialize() // Вызывается сразу

>  {/*...*/}                    // после конструирования


>publiс:

> template

> static shared_ptr Create() // Интерфейс для

> {                             // создания объектов

>  shared_ptr p(new T);

>  p->PostInitialize();

>  return p;

> }

>};


>class D : public B { /* ... */ }; // Некоторый производный

>                                  // класс


>shared_ptr p = D::Create(); // Создание объекта D

Этот не вполне надежный дизайн основан на ряде компромиссов.

• Производные классы, такие как >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 открытый и невиртуальный (свойства по умолчанию), он может быть случайно вызван для указателя, который в действительности указывает на производный объект, и в этом случае мы получим неопределенное поведение. Такое состояние дел привело к тому, что в старых стандартах кодирования требовалось делать деструкторы всех без исключения базовых классов виртуальными. Однако это уже перебор (даже если это и оправдано в общем случае); вместо этого следует использовать правило, согласно которому деструктор базового класса должен быть виртуальным тогда и только тогда, когда он открытый.