Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ static factory method vs constructor: how to avoid copying?

Tags:

c++

oop

This question asks for a clean way of implementing a static factory method in C++, and this answer describes a clear way to do so. Return Value Optimization would save us from making an unnecesary copy of Object, thus making this way of creating an Object as efficient as directly invoking a constructor. The overhead of copying i to id inside a private constructor is negligible because it's a small int.

However, the question and answer don't cover a more complex case when Object contains an instance variable that is an instance of class Foo (that requires complex initialization logic) rather than a small primitive type. Suppose I want to construct Foo using the arguments passed to Object. A solution using a constructor would look something like:

class Object {
    Foo foo;

public:
    Object(const FooArg& fooArg) {
        // Create foo using fooArg here
        foo = ...
    }
}

An alternative with a static factory method analogous to the quoted answer would be, as it appears to me:

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);
    }
}

Here, the overhead of copying foo_ to foo is no longer necessarily negligible, since Foo can be an arbitrarily complex class. Moreover, as far as I understand (C++ newbie here so I may be wrong), this code implicitly requires for a copy constructor to be defined for Foo.

What would be a similarly clean but also efficient way of implementing this pattern in this case?

To anticipate possible questions about why this is relevant, I consider having constructors with logic more complicated than just copying the arguments to be an anti-pattern. I expect the constructor to:

  • be guaranteed to work and not throw exceptions,
  • and not do heavy calculations under the hood.

Thus, I prefer to put complex initialization logic into static methods. Moreover, this approach provides additional benefits such as overloading by static factory method name even when the input argument types are the same, and the possibility of clearly stating what is being done inside in the name of the method.

like image 381
Vossler Avatar asked Jun 20 '18 06:06

Vossler


People also ask

What is the advantage of using a static factory method instead of a constructor to create an object?

Advantage: -One advantages of static factory methods is that, unlike constructors, they have names. -A second advantages of static factory methods is that, unlike constructors, they are not equired to create a new object each time they're invoked. -They can return an object of any subtype of their return type.

Why would one use the factory method rather than just using a constructor?

A factory method can return an already created object, unlike a constructor, which always creates a new instance.

Which of the following options is a disadvantage of using a static factory method?

The main disadvantage of providing only static factory methods is that classes without public or protected constructors cannot be subclassed.

When would you use static factory method?

A static factory method is a public static method on the object that returns a new instance of the object. These type of methods share the same benefits as the traditional factory method design pattern. This is especially useful for value objects that don't have a separate interface and implementation class.


2 Answers

Thanks to move constructor, you might do:

class Object {
    Foo foo;

    explicit Object(Foo&& foo_) : foo(std::move(foo_)) {}

public:
    static Object FromFooArg(const FooArg& fooArg) {
        // Create foo using fooArg here
        Foo foo = ...
        return Object(std::move(foo));
    }
};

If Foo is not movable, wrapping it in smart pointer is a possibility:

class Object {
    std::unique_ptr<Foo> foo;

    explicit Object(std::unique_ptr<Foo>&& foo_) : foo(std::move(foo_)) {}

public:
    static Object FromFooArg(const FooArg& fooArg) {
        // Create foo using fooArg here
        std::unique_ptr<Foo> foo = ...
        return Object(std::move(foo));
    }
};
like image 84
Jarod42 Avatar answered Dec 20 '22 09:12

Jarod42


What is wrong with initializing the instance in the constructor directly from the arguments needed to do so?

class Object
{
    Foo foo;                         // or const Foo foo, disallowing assignment

public:

    explicit Object(FooCtorArgs const&fooArg,
                    const AdditionalData*data = nullptr)
      : foo(fooArg)                  // construct instance foo directly from args
    {
        foo.post_construction(data); // optional; doesn't work with const foo
    }

    static Object FromFooArg(FooCtorArgs const&fooArg,
                             const AdditionalData*data = nullptr)
    { 
        return Object{fooArg,data};  // copy avoided by return value optimization
    }
};

AFAICT, there is no need to copy/move anything, even if you need to adjust foo post construction.

like image 26
Walter Avatar answered Dec 20 '22 09:12

Walter