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

Vos*_*ler 13 c++ oop

这个问题要求在C++中实现静态工厂方法的简洁方法,这个答案描述了一种明确的方法.返回值优化将使我们免于制作不必要的副本Object,从而使这种方式创建Object与直接调用构造函数一样高效.复制iid私有构造函数内部的开销可以忽略不计,因为它很小int.

但是,当Object包含一个实例变量Foo(它需要复杂的初始化逻辑)而不是一个小的基本类型时,问题和答案不会涵盖更复杂的情况.假设我想Foo使用传递给的参数构造Object.使用构造函数的解决方案看起来像:

class Object {
    Foo foo;

public:
    Object(const FooArg& fooArg) {
        // Create foo using fooArg here
        foo = ...
    }
}
Run Code Online (Sandbox Code Playgroud)

使用类似于引用答案的静态工厂方法的替代方案将是,因为在我看来:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

这里,复制foo_到的开销foo不再可以忽略不计,因为它Foo可以是任意复杂的类.而且,据我所知(这里我可能是错误的C++ newbie),这段代码隐含地要求为其定义一个拷贝构造函数Foo.

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

为了预测可能的问题,为什么这是相关的,我认为构造函数的逻辑比复制参数是一个反模式更复杂.我希望构造函数:

  • 保证工作,不抛出异常,
  • 并没有在引擎盖下做大量的计算.

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

Jar*_*d42 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));
    }
};
Run Code Online (Sandbox Code Playgroud)

如果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));
    }
};
Run Code Online (Sandbox Code Playgroud)


Wal*_*ter 6

直接从所需的参数初始化构造函数中的实例有什么问题?

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
    }
};
Run Code Online (Sandbox Code Playgroud)

fooAFAICT,即使您需要调整后期施工,也无需复制/移动任何内容。