考虑这个课程:
class Widget
{
Widget::Widget();
bool initialize();
}
Widget
具有以下特征:
initialize()
才能完全构建initialize()
可能会失败initialize()
很贵鉴于此,我在工厂函数中封装创建,该函数始终返回相同的Widget
实例:
Widget* widget() {
static auto w = new Widget;
static auto initialized = false;
if (!initialized) {
if (!w->initialize()) {
return nullptr;
}
initialized = true;
}
return w;
}
widget()
的返回类型应该是什么?
特别是,我想以某种方式明确表示返回的Widget
的生命周期将超过任何调用者,但不会引用内部实现。
std::shared_ptr<Widget>
。这是自我记录的,但我不喜欢它会引入完全不必要的引用计数开销。std::unique_ptr<Widget>
。如果调用者将其转换为shared_ptr
,我认为这与#2具有相同的感知问题。答案 0 :(得分:11)
我投票支持:
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;
}
它清楚地表明来电者不以任何方式拥有Widget
,并且不用担心调用者delete
- Widget
,并且清楚呼叫是否成功。
答案 1 :(得分:7)
不是原始指针在这里做正确的事吗?它已经表达了限制。它可能会失败(通过返回nullptr),并且因为它没有对指针做出任何承诺,所以调用者不能安全地导致它被删除。您正在获取原始指针,您不能假设您已经允许对指向对象的生命周期做出任何陈述。
答案 2 :(得分:1)
Herb Sutter在这种情况下的建议(http://herbsutter.com/2013/05/30/gotw-90-solution-factories/的第4项)是返回optional
。
函数可能返回指针的另一个原因是,返回nullptr以指示无法生成对象。通常,如果我们无法加载窗口小部件,最好抛出异常来报告错误。但是,如果无法加载窗口小部件是正常操作并且不应该被视为错误,则返回一个可选项,并且如果不需要报告其他类型的错误,则可能使工厂无法通过返回空的可选项进行良好通信
答案 3 :(得分:1)
正如其他人所指出的,如果工厂只生产一件产品,那么工厂也许不是正确的术语。这似乎是一个单身人士。
考虑到:
我会尝试这样的事情:
class Widget {
public:
static Widget& Instance() {
static Widget w{};
return w;
}
private:
Widget() {
// Expensive construction
}
Widget(const Widget&) = delete; // avoid copy
};
答案 4 :(得分:0)
为了让生命和所有权更加清晰,我会使用Singleton pattern的约定,并使您的函数成为getInstance
类上的静态Widget
函数。
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;
}
};
我认为原始指针返回类型记录了调用者不会获得所有权的事实,它可能为null。