没有unique_ptr的现代C ++构建器模式

时间:2018-01-30 18:25:16

标签: c++ builder

我想在现代C ++中实现构建器模式。来自Java背景,这是我想要模仿的东西:

// Usage
FooBuilder builder;
builder.setArg1(a);
builder.setArg2(b);
Foo foo = builder.build();

// Implementation
public class FooBuilder {
    // ...
    public Foo build() {
        return new Foo(a, b);
    }
}

典型的旧教科书只是建议一个人在C ++中这样做:

class FooBuilder {
    // ...
    Foo* build() {
        return new Foo(m_a, m_b);
    }
}

这显然不是一个好主意,因为处理原始指针可能容易出错。到目前为止,我提出的最好的方法是手动使用std::unique_ptr

class FooBuilder {
    // ...
    std::unique_ptr<Foo> build() {
        return std::make_unique<Foo>(m_a, m_b);
    }
}

// Usage
auto fooPtr = builder.build();
Foo& foo = *fooPtr;
foo.someMethod();

它更好,因为它不需要手动delete,这个双线程转换为引用是丑陋的,更重要的是,它使用堆分配,而简单的无构建器版本将完全没问题只需一个简单的堆栈分配:

Foo foo(..., ...); // <= on stack

有没有更好的方法可以做到这一点,即没有unique_ptr,或者为Foo进行某种堆栈分配?

2 个答案:

答案 0 :(得分:5)

没有理由让 在堆上分配以使用构建器模式。只需让您的build()方法直接返回Foo

class FooBuilder {
public:
    Foo build() { // You may consider having a &&-qualified overload
        return Foo{ ..., ... };
    }
};

答案 1 :(得分:4)

通常,如果Foo是copy_constructible,那么您可以按值返回Foo。

#include <type_traits>

class Foo
{
    int i;

public:
    Foo(int i): i(i){}

};

static_assert(std::is_copy_constructible<Foo>::value, "Foo is copy-constructible");

struct FooFactory
{
    //...
    Foo build() {return Foo(1);}
};


int main()
{
    FooFactory factory;
    //...
    Foo foo = factory.build();
}

c ++ 17中的新内容是guaranteed copy elision,这意味着即使类型没有复制或移动构造函数,也可以按值返回:

#include <type_traits>

class Foo
{
    int i;

public:
    Foo(int i): i(i){}
    // regular copy constructors don't exist for whatever reason. 
    Foo() = delete;
    Foo(Foo const& ) =delete;
    Foo(Foo&& ) = delete;
    Foo& operator=(Foo const&) = delete;
    Foo& operator=(Foo&& ) = delete;
};

static_assert(not std::is_copy_constructible<Foo>::value, "Foo is definitely not copy-constructible");


struct FooFactory
{
    //...
    Foo build() {return Foo(1);}
};


int main()
{
    FooFactory factory;
    //...
    Foo foo = factory.build();
}