C ++静态工厂方法与构造函数:如何避免复制?

时间:2018-06-20 06:50:37

标签: c++ oop

This question要求一种干净的方法来在C ++中实现静态工厂方法,this answer描述了一种清晰的方法。返回值优化将使我们免于制作不必要的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定义一个副本构造函数。

在这种情况下,实现此模式的一种同样干净但有效的方法是什么?

为了预料到为什么这可能是相关的问题,我认为让逻辑构造函数比将参数复制为反模式要复杂得多。我希望构造函数:

  • 保证工作并且不会引发异常,
  • 并且不要在后台进行繁琐的计算。

因此,我更喜欢将复杂的初始化逻辑放入静态方法中。而且,这种方法还提供了其他好处,例如即使输入参数类型相同,也可以通过静态工厂方法名称进行重载,还可以在方法名称中清楚地说明正在执行的操作。

2 个答案:

答案 0 :(得分:6)

要移动构造函数,您可以这样做:

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

如果Foo不可移动,则可以将其包装在智能指针中:

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

答案 1 :(得分:4)

直接从构造器中的实例中通过所需的参数直接初始化实例的错误是什么?

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,即使您需要调整foo的帖子构造,也无需复制/移动任何内容。