我正在考虑使用"自杀对象"模拟游戏中的实体,即能够自行删除的对象。现在,通常的C ++ 03实现(普通的旧delete this
)对可能引用自杀对象的其他对象没有任何作用,这就是我使用std::shared_ptr
和std::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。
我不打算在多线程代码中使用它。如果有需要,我一定会重新考虑整件事。
答案 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之后,如果你决定你做错了,外部伪规则接口会有不同的内容。
答案 1 :(得分:3)
不是直接答案,但可能是有用的信息。
在Chromium代码库中,有一个概念正是您要实现的目标。他们称之为WeakPtrFactory。他们的解决方案不能直接用于您的代码,因为他们有自己的实现,例如shared_ptr
和weak_ptr
,但设计明智,它对您有用。
我尝试实现它,并发现双重删除的问题可以通过传入内部shared_ptr
自定义空删除器来解决 - 从这一刻起,既不会创建从weak_ptr
创建的shared_ptrs而不是内部shared_ptr
将能够(再次)在您的对象上调用析构函数。
要解决的唯一问题是,如果您的对象被删除,而其他地方保留shared_ptr
该怎么办?但是从我看来它不能简单地通过任何神奇的手段解决,并且需要以完全没有发生的方式设计整个项目,例如通过仅在本地范围内使用shared_ptr
并确保某些操作(创建自杀对象,使用它,命令其自杀)只能在同一个线程中执行。
答案 2 :(得分:2)
我理解你正试图为SO创建一个最小的例子,但我看到了一些你想要考虑的挑战:
我赞赏你提前考虑内存管理。有纪律地使用shared_ptr和weak_ptr将有助于解决您的内存管理问题 - 我建议不要试图让实例尝试管理自己的生命周期。
至于艺术研究......我只是建议,如果这真的是你的激情!祝你好运!