Этот вопрос требует чистого способа реализации статического заводского метода в C++, и этот ответ описывает четкий способ сделать это. Оптимизация возвращаемого значения спасла бы нас от создания ненужной копии Object
, тем самым создав такой способ создания Object
так же эффективно, как непосредственный вызов конструктора. Накладные расходы на копирование i
в id
внутри частного конструктора незначительны, потому что это небольшой int
.
Однако вопрос и ответ не охватывают более сложный случай, когда Object
содержит переменную экземпляра, которая является экземпляром класса Foo
(который требует сложной логики инициализации), а не малым примитивным типом. Предположим, что я хочу построить Foo
используя аргументы, переданные Object
. Решение с использованием конструктора будет выглядеть примерно так:
class Object {
Foo foo;
public:
Object(const FooArg& fooArg) {
// Create foo using fooArg here
foo = ...
}
}
Альтернативой со статическим заводским методом, аналогичным цитируемому ответу, было бы, как мне кажется:
class Object {
Foo foo;
explicit Object(const Foo& foo_):
foo(foo_)
{
}
public:
static Object FromFooArg(const FooArg& fooArg) {
// Create foo using fooArg here
Foo foo = ...
return Object(foo);
}
}
Здесь накладные расходы на копирование foo_
на foo
больше не обязательно незначительны, так как Foo
может быть произвольно сложным классом. Более того, насколько я понимаю (C++ новичок здесь, поэтому я могу ошибаться), этот код неявно требует, чтобы конструктор копирования был определен для Foo
.
Что было бы таким же чистым, но и эффективным способом реализации этой модели в этом случае?
Чтобы предвидеть возможные вопросы о том, почему это уместно, я считаю, что конструкторы с логикой сложнее, чем просто копирование аргументов как анти-шаблон. Я ожидаю, что конструктор:
- гарантированно работать, а не бросать исключения,
- и не делайте тяжелых расчетов под капотом.
Таким образом, я предпочитаю ставить сложную логику инициализации в статические методы. Более того, такой подход обеспечивает дополнительные преимущества, такие как перегрузка по имени статического заводского метода, даже если типы входных аргументов одинаковы, и возможность четко указывать, что делается внутри имени метода.