这个函数应该返回什么类型的对象?

时间:2015-01-25 23:38:06

标签: c++ c++11 factory smart-pointers lifetime

考虑这个课程:

class Widget
{
    Widget::Widget();
    bool initialize();
}

Widget具有以下特征:

    必须调用
  1. initialize()才能完全构建
  2. initialize()可能会失败
  3. initialize()很贵
  4. 鉴于此,我在工厂函数中封装创建,该函数始终返回相同的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的生命周期将超过任何调用者,但不会引用内部实现。

    1. 返回一个原始指针,并添加一条注释,指出“返回的指针指向一个具有静态存储持续时间的对象,该对象在程序结束前不会被删除”。这很简单,但不能自我记录。
    2. 返回std::shared_ptr<Widget>。这是自我记录的,但我不喜欢它会引入完全不必要的引用计数开销。
    3. 使用自动删除功能返回std::unique_ptr<Widget>。如果调用者将其转换为shared_ptr,我认为这与#2具有相同的感知问题。

5 个答案:

答案 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)

正如其他人所指出的,如果工厂只生产一件产品,那么工厂也许不是正确的术语。这似乎是一个单身人士。

考虑到:

  • 我们只会创建一个Widget实例
  • 该实例将在第一次有人要求时构建(如果有的话)
  • 该实例将一直存在,直到程序结束,然后才能销毁
  • 没有人应该删除实例

我会尝试这样的事情:

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。