懒惰构造的shared_ptr

Car*_*ijn 7 c++ std wrapper shared-ptr c++11

编辑:完全重新编辑,因为原来是一个非结构化的混乱:)感谢大家的输入到目前为止; 我希望我在下面的文本中使用它.

我正在寻找一个懒惰创建的可共享指针.我有一个假设的大班Thing.事情是巨大的,因此制造成本很高,但是虽然它们在代码中的任何地方使用(共享,经过大量传递,修改,存储供以后使用等),但实际上它们实际上并没有被使用,所以它们实际上是在延迟创建直到实际访问它们是可取的.因此,需要懒惰地创造,并且需要可分享.让我们调用这个封装指针包装器SharedThing.

class SharedThing {
  ...
  Thing* m_pThing;
  Thing* operator ->() {
    // ensure m_pThing is created
    ...
    // then
    return m_pThing
  );
}
...
SharedThing pThing;
...
// Myriads of obscure paths taking the pThing to all dark corners
// of the program, some paths not even touching it
...
if (condition) {
  pThing->doIt();   // last usage here
}
Run Code Online (Sandbox Code Playgroud)

要求

  1. 实际事物的实例化必须尽可能延迟; 只有在首次取消引用SharedThing时才会创建事物
  2. SharedThing必须安全使用,因此不需要工厂方法
  3. SharedThing必须具有shared_ptr(like)接口
  4. 与尚未创建的SharedThing共享必须实际共享要创建的Thing,但Thing的实例化必须再次延迟,直到需要
  5. 使用SharedThings必须尽可能简单(最好是100%透明,就像使用实际的东西一样)
  6. 它必须有点高效

到目前为止,我们已经提出了四种选择:

选项1

typedef std::shared_ptr<Thing> SharedThing;
SharedThing newThing() {
  return make_shared<Thing>();
}
...
// SharedThing pThing; // pThing points to nullptr, though...
SharedThing pThing(new Thing()); // much better
SharedThing pThing = newThing(); // alternative
Run Code Online (Sandbox Code Playgroud)
  1. 0%得分; 从一开始就需要一个Thing实例
  2. 0%得分; 你可以说SharedThing pThing; 但这对事情有点过于担心
  3. 这里100%得分;)
  4. na由于第1点
  5. 100%得分
  6. 0%得分,因为在任何地方创建所有的东西(即使没有使用)是性能的消耗,这正是我问这个问题的原因:)

第1点和第6点缺乏得分是一个杀手; 没有选择1.

选项2

class SharedThing: public shared_ptr<Thing> {};
Run Code Online (Sandbox Code Playgroud)

并覆盖特定成员以确保在取消引用shared_ptr时,它会及时创建Thing.

  1. 也许可以通过覆盖正确的成员来实现(取决于stl的实现),但我认为这很快变得一团糟
  2. 100%得分
  3. 100%得分,虽然模仿所有的构造函数和运算符是相当一些工作
  4. 不知道这是否可以......
  5. 100%得分
  6. 100%得分,如果内部事情巧妙地完成

这个选项优于1并且可能没问题,但看起来很混乱和/或黑客......

选项3.1

class SharedThing {
  std::shared_ptr<Thing> m_pThing;
  void EnsureThingPresent() {
    if (m_pThing == nullptr) m_pThing = std::make_shared<Thing>();
  }
public:
  SharedThing(): m_pThing(nullptr) {};
  Thing* operator ->() {
    EnsureThingCreated();
    return m_pThing.get();
  }
}    
Run Code Online (Sandbox Code Playgroud)

并为operator*和const版本添加额外的包装方法.

  1. 100%得分
  2. 100%得分
  3. 可以,但必须单独创建所有接口成员
  4. 0%得分; 当附加到nullptr'ed SharedThing(例如operator =)时,它需要首先创建Thing以便能够共享
  5. 再次100%得分
  6. 50%得分; 2个间接

这个人在4岁时惨遭失败,所以这个也是关闭的.

选项3.2

class SharedThing {
  typedef unique_ptr<Thing> UniqueThing;
  shared_ptr<UniqueThing> m_pThing;
}
Run Code Online (Sandbox Code Playgroud)

并添加3.1中的所有其他方法

  1. 100%得分
  2. 100%得分
  3. 可以,但必须单独创建所有接口成员
  4. 100%得分
  5. 再次100%得分
  6. 25%得分?我们这里有3个间接......

除了建议的性能(但需要测试)之外,这似乎没问题.

选项4

class LazyCreatedThing {
  Thing* m_pThing;
}
typedef shared_ptr<LazyCreatedThing> SharedThing;
SharedThing makeThing() {
  return make_shared<LazyCreatedThing>();
}
Run Code Online (Sandbox Code Playgroud)

并添加各种运算符 - >重载,使LazyCreatedThing看起来像一个东西*

  1. 100%得分
  2. 与上面的选项1相同的缺点
  3. 100%在这里毫不费力地得分
  4. 100%得分
  5. 0%得分; 取消引用SharedThing会产生一个LazyCreatedThing,所以即使它可能有它的运算符 - >用于访问Thing,它也永远不会被链接,导致(*pThing) - > doIt();
  6. 25-50%得分?我们这里有3个间接,如果我们可以使用std :: make_shared则有2个

在这里惨败5,这使得这是一个禁忌.

结论

到目前为止最好的选择似乎是3.2; 让我们看看我们还能想出什么!:)

5go*_*der 0

也许我误解了这个问题,但问题就不能这么简单吗?

\n\n
class Factory\n{\nprivate:\n\n  std::shared_ptr<My1stType> my1st_ {};\n  std::shared_ptr<My2ndType> my2nd_ {};\n  std::shared_ptr<My3rdType> my3rd_ {};\n  // \xe2\x80\xa6\n\npublic:\n\n  std::shared_ptr<My1stType>\n  get1st()\n  {\n    if (!this->my1st_)\n      this->my1st_ = std::make_shared<My1stType>(/* \xe2\x80\xa6 */);\n    return this->my1st_;\n  }\n\n  // \xe2\x80\xa6\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

然而,如上所示,这不是线程安全的,以防这对您很重要。

\n