Используйте фабрику, если вам нужно "виртуальное поведение" во время инициализации
Причина
Если состояние объекта базового класса должно зависеть от состояния производной части объекта, нам нужно использовать виртуальную функцию (или эквивалент) при минимизации возможности неправильного использования несовершенно построенного объекта.
Примечание
Тип возвращаемого значения фабрики должен быть unique_ptr по умолчанию; если некоторые использования являются общими, вызывающий может переместить unique_ptr в shared_ptr. Однако если автор фабрики знает, что все использования возвращаемого объекта будут общими использованиями, верните shared_ptr и используйте make_shared в теле, чтобы сэкономить распределение.
Пример, плохой
class B {
public:
B()
{
/* ... */
f(); // ПЛОХО: C.82: Не вызывайте виртуальные функции в конструкторах и деструкторах
/* ... */
}
virtual void f() = 0;
};
Пример
class B {
protected:
class Token {};
public:
explicit B(Token) { /* ... */ } // создать несовершенно инициализированный объект
virtual void f() = 0;
template<class T>
static shared_ptr<T> create() // интерфейс для создания общих объектов
{
auto p = make_shared<T>(typename T::Token{});
p->post_initialize();
return p;
}
protected:
virtual void post_initialize() // вызывается сразу после конструктора
{ /* ... */ f(); /* ... */ } // ХОРОШО: виртуальная диспетчеризация безопасна
};
class D : public B { // некоторый производный класс
protected:
class Token {};
public:
explicit D(Token) : B{ B::Token{} } {}
void f() override { /* ... */ };
protected:
template<class T>
friend shared_ptr<T> B::create();
};
shared_ptr<D> p = D::create<D>(); // создание объекта D
make_shared требует, чтобы конструктор был общедоступным. Требуя защищённый Token, конструктор больше не может быть публично вызван, поэтому мы избегаем того, чтобы несовершенно построенный объект вышел в дикую природу. Предоставляя функцию фабрики create(), мы делаем конструирование (в свободном хранилище) удобным.
Примечание
Обычные функции фабрики распределяют память в свободном хранилище, а не в стеке или во вложенном объекте.
См. также: Обсуждение