I'd want to implement a builder pattern in modern C++. Coming from Java background, this is something I'd like to emulate:
// 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);
}
}
Typical older textbooks just advice one to do it like that in C++:
class FooBuilder {
// ...
Foo* build() {
return new Foo(m_a, m_b);
}
}
which is obviously not a good idea, as dealing with raw pointers might be error-prone. The best I've came up with so far is using std::unique_ptr
manually:
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();
It's better, as it doesn't require manual delete
, this two-liner conversion to reference is ugly, and, what's more important, it uses heap allocation, while simple builder-less version would be totally ok with just a simple stack allocation:
Foo foo(..., ...); // <= on stack
Are there any better ways to do that, ie without unique_ptr, or with some sort of on-stack allocation for Foo?
There's no reason why you have to allocate on the heap to use the builder pattern. Just have your build()
method return Foo
directly:
class FooBuilder {
public:
Foo build() { // You may consider having a &&-qualified overload
return Foo{ ..., ... };
}
};
Generally, if Foo is copy_constructible , then you can just return Foo by value.
#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();
}
And new in c++17, is guaranteed copy elision , which means that you can return by value even if the type does not have copy or move constructors:
#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();
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.