公开类所拥有资源的正确方法是什么?

Ven*_*emo 20 c++ ownership unique-ptr c++11

假设我有一个有Document类的库.的一个实例Document可以拥有的几个实例Field.Field有多个子类(例如IntegerFieldStringField),甚至API用户也可以将它子类化并提供子类实例Document(假设用户可以开发一个自定义类型的数据来存储在一个字段中).

我希望通过API 公开所Field拥有的实例Document,以便用户可以与它们进行交互,但不会转移所有权.

这样做的正确方法是什么?

我想过:

  • 揭露const std::unique_ptr<Field>&参考 - 这感觉非常难看
  • 公开Field*指针 - 这感觉不对,因为用户可能不确定他是否应该删除实例
  • std::shared_ptr相反使用- 这感觉很糟糕,因为所有权并非真正共享

例如,

class Document {
private:
    std::map<std::string, std::unique_ptr<Field> > fields;
    // ...
public:
    // ...

    // How is this done properly?
    const std::unique_ptr<Field> &field(const std::string &name) {
        return fields[name];
    }
}
Run Code Online (Sandbox Code Playgroud)

我期待着你的建议.
(我也欢迎有关@Fulvio建议的替代方法的建议.)

Mik*_*our 13

我会回来Bar&(也许是一个const).

如果从地图中删除元素,用户将需要了解该引用将无效 - 但由于单一所有权模型,无论您采取何种情况都是如此.

  • (从我的旧评论)这也有利于暴露C++ 98兼容接口,(如果你使用impl惯用法)允许你与非C++ 11代码接口. (2认同)

小智 6

正如其他人从技术角度回答的那样,我想指出一种不同的方法并修改你的设计.我们的想法是尝试尊重Demeter法则,并且不允许访问对象的子组件.这有点难,没有具体的例子,我不能提供很多细节,但试图想象一个由Pages组成的类书.如果我想打印本书的一页,两页或更多页面,我可以使用您当前的设计:

auto range = ...;
for( auto p : book.pages(range) )
  {
  p->print();
  }
Run Code Online (Sandbox Code Playgroud)

在遵守得墨忒耳的同时,你将拥有

auto range = ...;
book.print( /* possibly a range here */ );
Run Code Online (Sandbox Code Playgroud)

这是倾向于更好的封装,因为您不依赖于书类的内部细节,如果其内部结构发生变化,您不需要对您的客户端代码执行任何操作.