利用`std :: weak_ptr`的自杀对象实现

时间:2015-01-09 17:54:29

标签: c++ shared-ptr language-lawyer ownership

我正在考虑使用"自杀对象"模拟游戏中的实体,即能够自行删除的对象。现在,通常的C ++ 03实现(普通的旧delete this)对可能引用自杀对象的其他对象没有任何作用,这就是我使用std::shared_ptrstd::weak_ptr的原因。

现在进行代码转储:

#include <memory>
#include <iostream>
#include <cassert>

struct SuObj {
    SuObj() { std::cout << __func__ << '\n'; }
    ~SuObj() { std::cout << __func__ << '\n'; }

    void die() {
        ptr.reset();
    }

    static std::weak_ptr<SuObj> create() {
        std::shared_ptr<SuObj> obj = std::make_shared<SuObj>();
        return (obj->ptr = std::move(obj));
    }

private:

    std::shared_ptr<SuObj> ptr;
};

int main() {
    std::weak_ptr<SuObj> obj = SuObj::create();

    assert(!obj.expired());
    std::cout << "Still alive\n";

    obj.lock()->die();

    assert(obj.expired());
    std::cout << "Deleted\n";

    return 0;
}

问题

此代码似乎工作正常。但是,我希望有别人的眼光来衡量它。这段代码有意义吗?我是否盲目地驶入未定义的土地?我应该放下键盘并立即开始艺术研究吗?

我希望这个问题能够充分缩小。看起来有点微小而且低级别的CR。

次要精度

我不打算在多线程代码中使用它。如果有需要,我一定会重新考虑整件事。

3 个答案:

答案 0 :(得分:3)

当你有基于shared_ptr的对象生命周期时,对象的生命周期是集体拥有它的shared_ptr联盟的“生命周期”。

在您的情况下,您有一个内部shared_ptr,在内部shared_ptr到期之前,您的对象不会消亡。

然而,这并不意味着你可以自杀。如果您删除了最后一个引用,那么您的对象将继续存在如果有人.lock()代表weak_ptr并存储结果。由于这是您可以从外部访问对象的唯一方法,因此可能会发生 1

简而言之,die()无法杀死该对象。它最好被称为remove_life_support(),因为在删除所述生命支持后,其他东西可以使对象保持活着。

除此之外,你的设计也有效。


1 您可以说“好吧,然后调用者不应该只保留shared_ptr” - 但这不起作用,因为只要shared_ptr持续存在,对象有效的检查才有效。另外,通过公开创建shared_ptr的方式,您没有类型保证客户端代码不会存储它们(意外或故意)。

基于事务的模型(在其中传递lambda,并在内部对其进行操作)如果你想要严重的偏执稳健性,可以帮助解决这个问题。

或者你可以忍受有时生活太久的物体。


考虑将这些混乱的细节隐藏在常规类型(或几乎常规)后面,这些细节具有pImpl的讨厌的内存管理问题。 pImpl可以是具有上述语义的weak_ptr

然后,您的代码用户只需要与Regular(或pseudoRegular)包装器进行交互。

如果您不希望克隆变得容易,请禁用复制构造/分配并仅显示移动。

现在你讨厌的内存管理隐藏在一个fascade之后,如果你决定你做错了,外部伪规则接口会有不同的内容。

Regular type in C++11

答案 1 :(得分:3)

不是直接答案,但可能是有用的信息。

在Chromium代码库中,有一个概念正是您要实现的目标。他们称之为WeakPtrFactory。他们的解决方案不能直接用于您的代码,因为他们有自己的实现,例如shared_ptrweak_ptr,但设计明智,它对您有用。

我尝试实现它,并发现双重删除的问题可以通过传入内部shared_ptr自定义空删除器来解决 - 从这一刻起,既不会创建从weak_ptr创建的shared_ptrs而不是内部shared_ptr将能够(再次)在您的对象上调用析构函数。

要解决的唯一问题是,如果您的对象被删除,而其他地方保留shared_ptr该怎么办?但是从我看来它不能简单地通过任何神奇的手段解决,并且需要以完全没有发生的方式设计整个项目,例如通过仅在本地范围内使用shared_ptr并确保某些操作(创建自杀对象,使用它,命令其自杀)只能在同一个线程中执行。

答案 2 :(得分:2)

我理解你正试图为SO创建一个最小的例子,但我看到了一些你想要考虑的挑战:

  1. 您有一个公共构造函数和析构函数,因此从技术上讲,并不能保证始终使用create()方法。
  2. 您可以将这些内容设为受保护或私有,但该决定会干扰使用std算法和容器。
  3. 这并不能保证对象实际上会被破坏,因为只要有人拥有shared_ptr它就会存在。对于您的用例而言,这可能是也可能不是问题,但正因为如此,我认为这不会为您带来更多的价值。
  4. 这可能会让其他开发人员感到困惑和反直觉。它可能会使维护更加困难,即使您的目的是使其更容易。这是一种价值判断,但我鼓励您考虑它是否真的更容易管理。
  5. 我赞赏你提前考虑内存管理。有纪律地使用shared_ptr和weak_ptr将有助于解决您的内存管理问题 - 我建议不要试图让实例尝试管理自己的生命周期。

    至于艺术研究......我只是建议,如果这真的是你的激情!祝你好运!