Reputation: 9422
Consider this class:
class Widget
{
Widget::Widget();
bool initialize();
}
A Widget
has the following characteristics:
initialize()
must be invoked to fully constructinitialize()
may failinitialize()
is expensiveGiven that, I am encapsulating creation in factory function that always returns the same Widget
instance:
Widget* widget() {
static auto w = new Widget;
static auto initialized = false;
if (!initialized) {
if (!w->initialize()) {
return nullptr;
}
initialized = true;
}
return w;
}
What should the return type of widget()
be?
In particular, I'd like to somehow make it clear that the lifetime of the returned Widget
will outlast any caller, but without referencing the internal implementation.
std::shared_ptr<Widget>
. This is self-documenting, but I don't like that it will introduce completely unnecessary reference counting overhead.std::unique_ptr<Widget>
with a custom deleter function that is a no-op. I think this has the same perceived problem as #2 if the caller converts it into a shared_ptr
.Upvotes: 8
Views: 297
Reputation: 351
As other people noted if the factory will only produce one item, factory perhaps is not the right term. It seems a Singleton.
Taking in account that:
I'll try something like this:
class Widget {
public:
static Widget& Instance() {
static Widget w{};
return w;
}
private:
Widget() {
// Expensive construction
}
Widget(const Widget&) = delete; // avoid copy
};
Upvotes: 1
Reputation: 15334
To make lifetime and ownership clearer I would use the conventions of the Singleton pattern and make your function a static getInstance
function on the Widget
class.
class Widget {
bool initialize();
public:
static Widget* getInstance() {
static Widget w;
static bool initialized = false;
if (!initialized) {
if (!w.initialize()) {
return nullptr;
}
initialized = true;
}
return &w;
}
};
I think a raw pointer return type documents the fact that the caller is not expected to take ownership and it could be null.
Upvotes: 0
Reputation: 25581
Herb Sutter's recommendation in this case (item 4 at http://herbsutter.com/2013/05/30/gotw-90-solution-factories/) is to return optional
.
There could be one additional reason the function might have returned a pointer, namely to return nullptr to indicate failure to produce an object. Normally it’s better throw an exception to report an error if we fail to load the widget. However, if not being able to load the widget is normal operation and should not be considered an error, return an optional, and probably make the factory noexcept if no other kinds of errors need to be reported than are communicated well by returning an empty optional.
Upvotes: 1
Reputation: 780
Isn't a raw pointer the right thing to do here? It expresses the restrictions already. It can fail (by returning nullptr), and since it makes no promises about the pointer, callers can't safely cause it to be deleted. You're getting a raw pointer, you can't make the assumption that you're allowed to make any statements about the lifetime of the pointed-to object.
Upvotes: 7
Reputation: 303216
I vote for:
boost::optional<Widget&> widget() {
static Widget w; // no reason for this to be a pointer
static bool initialized = false;
if (!initialized) {
if (!w.initialize()) {
return boost::none;
}
initialized = true;
}
return w;
}
It makes it clear that the caller doesn't own the Widget
in any way, there's no worry of the caller delete
-ing the Widget
, and it's clear whether or not the call succeeded.
Upvotes: 11