gpt4 book ai didi

c++ - 延迟构造的 shared_ptr

转载 作者:可可西里 更新时间:2023-11-01 18:38:25 28 4
gpt4 key购买 nike

编辑:完全重新编辑,因为原作变得一团糟:) 到目前为止,感谢大家的投入;我希望我把它写进了下面的文字中。



我正在寻找一个懒惰创建的可共享指针。我有一个假设的大类东西。东西很大,因此制作成本很高,但是虽然它们在代码中无处不在(共享、自由传递、修改、存储以备后用等),但它们往往最终没有真正使用,因此延迟了它们的实际使用在实际访问它们之前创建是可取的。因此,事物需要懒惰地创建,并且需要可共享。让我们称之为封装指针包装器 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
}

要求
  • 实际事物的实例化必须尽可能延迟;只有在第一次取消引用 SharedThing 时才会创建事物
  • SharedThing 必须使用安全,因此不需要工厂方法
  • SharedThing 必须有一个 shared_ptr (like) 接口(interface)
  • 与尚未创建的 SharedThing 共享实际上必须共享要创建的 Thing,但 Thing 的实例化必须再次延迟,直到需要时
  • 使用 SharedThings 必须尽可能简单(最好是 100% 透明,就像使用实际事物一样)
  • 它必须有点性能

  • 到目前为止,我们已经提出了四个选项:

    选项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
  • 0% 分数;从一开始就需要一个 Thing 实例
  • 0% 分数;你可以说 SharedThing pThing;但这有点过分担心
  • 此处 100% 得分 ;)
  • 不适用由于第 1 点
  • 100% 评分
  • 0% 分数,因为在任何地方创建所有东西(即使没有使用)会消耗性能,这正是我问这个问题的原因:)

  • 第 1 点和第 6 点的得分不足是这里的杀手。没有更多选项1。

    选项 2
    class SharedThing: public shared_ptr<Thing> {};

    并覆盖特定成员以确保当 shared_ptr 被取消引用时,它会及时创建 Thing。
  • 也许可以通过覆盖正确的成员(取决于 STL 的实现)来实现,但我认为这个速度很快就会变得一团糟
  • 100% 评分
  • 100% 的分数,虽然模仿所有的构造函数和运算符是相当多的工作
  • 不知道这是否可行...
  • 100% 评分
  • 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();
    }
    }

    并为运算符 * 和 const 版本添加额外的包装方法。
  • 100% 评分
  • 100% 评分
  • 可行,但必须单独创建所有接口(interface)成员
  • 0% 分数;当附加到 nullptr 的 SharedThing(例如 operator =)时,它需要先创建 Thing 才能共享
  • 再次100%评分
  • 50% 分数; 2 次间接访问

  • 这个在 4 上惨遭失败,所以这个也失败了。

    选项 3.2
    class SharedThing {
    typedef unique_ptr<Thing> UniqueThing;
    shared_ptr<UniqueThing> m_pThing;
    }

    并添加所有其他方法,如 3.1
  • 100% 评分
  • 100% 评分
  • 可行,但必须单独创建所有接口(interface)成员
  • 100% 评分
  • 再次100%评分
  • 25%的分数?我们在这里有 3 个间接...

  • 除了建议的性能外,这似乎还可以(不过需要测试)。

    选项 4
    class LazyCreatedThing {
    Thing* m_pThing;
    }
    typedef shared_ptr<LazyCreatedThing> SharedThing;
    SharedThing makeThing() {
    return make_shared<LazyCreatedThing>();
    }

    并添加各种运算符 -> 重载,使 LazyCreatedThing 看起来像 Thing*
  • 100% 评分
  • 与上述选项 1 相同的缺点
  • 在这里毫不费力地获得 100% 的分数
  • 100% 评分
  • 0% 分数;取消引用一个 SharedThing 会产生一个 LazyCreatedThing,所以即使它可能有它的 operator -> 来访问 Thing,它永远不会被链接,导致 (*pThing)->doIt();
  • 25-50%的分数?我们这里有 3 个间接寻址,如果我们可以使用 std::make_shared
  • 则有 2 个间接寻址。

    在 5 上惨遭失败使这成为禁忌。

    结论

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

    最佳答案

    我会实现 LazyThing wrapper 。我想适应要容易得多Thing界面,而不是 std::shared_ptr一。

    class Thing
    {
    public:
    void Do()
    {
    std::cout << "THING" << std::endl;
    }
    };

    class LazyThing
    {
    public:
    void Do()
    {
    getThing().Do();
    }

    private:
    Thing& getThing()
    {
    if (!thing_)
    thing_ = std::make_unique<Thing>();

    return *thing_;
    }

    std::unique_ptr<Thing> thing_;
    };

    现在,您可以将它与任何智能指针一起使用,甚至可以在堆栈上创建它:
    LazyThing lazy;
    auto sharedLazy = std::make_shared<LazyThing>();
    auto uniqueLazy = std::make_unique<LazyThing>();

    或者如您的示例所示:
    typedef std::shared_ptr<LazyThing> SharedLazyThing;

    SharedLazyThing newThing() {
    return std::make_shared<LazyThing>();
    }
    ...
    auto pThing = newThing();

    更新

    如果你想保证共享语义并且不打扰调用 newThing()或任何其他工厂方法,只需放弃 shared_ptr界面。那里不需要。

    实现 SharedLazyThing作为具有共享语义的值类型。棘手的事情是您需要添加另一个间接级别来提供共享 Thing 的懒惰构造。目的。
    class SharedLazyThing
    {
    using UniqueThing = std::unique_ptr<Thing>;

    public:
    void Do()
    {
    getThing().Do();
    }

    private:
    Thing& getThing()
    {
    if (!*thing_)
    *thing_ = std::make_unique<Thing>();

    return **thing_;
    }

    std::shared_ptr<UniqueThing> thing_ = std::make_shared<UniqueThing>();
    };

    现在,您可以简单地使用 SharedLazyThing到处。
    SharedLazyThing thing1;
    SharedLazyThing thing2(thing1);
    SharedLazyThing thing3 = thing1;

    关于c++ - 延迟构造的 shared_ptr,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32640282/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com