"delete this"分配给使用 std::shared_ptr 的对象?
"delete this" to an object that's allocated with std::shared_ptr?
我知道,只要你用new
分配一些东西,就可以用C++说delete this
,使用传统的指针。 事实上,我也知道,如果你小心处理它,这是一种很好的做法。 我可以让一个物体说delete this
它是否被std::shared_ptr
持有吗? 这应该称为析构函数,对吧? 为了给你一个想法,我正在制作一个游戏,其中一艘船可以发射导弹,我想让导弹自行删除。
不,这是不安全的,物体的寿命由shared_ptr
持有者决定,所以物体本身不能决定是否要死。如果你这样做,你会得到双倍最后shared_ptr
死亡时删除。我能提供的唯一解决方案是"重新考虑你的设计"(你可能一开始就不需要shared_ptr
,导弹可能是值或汇集的物体)。
导弹要删除自己,它必须拥有自己,或者至少与他人分享自己的所有权。 既然你说导弹有shared_ptr
,我假设你已经有多个物体共享导弹的所有权。
自己的shared_ptr
,从而分享自己的所有权。 然而,这将始终创建一个循环的所有权模式:只要导弹的shared_ptr数据成员引用自身,参考计数就永远不会下降到零,因此导弹就会泄漏。
你可以让外部物体或事件告诉导弹自行删除,但我不确定有什么意义。 为了告诉导弹删除自己,应该通过shared_ptr
进行通信,然后删除不会真正发生,直到shared_ptr
放开导弹。
是的,这是可能的。 不,我认为这不是一个好主意。 对我来说,它看起来很容易发生内存泄漏,实际上并没有增加价值。 但对于好奇的人来说,以下是您将如何做到这一点:
#include <iostream>
#include <memory>
class missile
: public std::enable_shared_from_this<missile>
{
std::shared_ptr<missile> self_;
public:
missile()
{}
~missile() {std::cout << "~missile()n";}
void set_yourself()
{
self_ = shared_from_this();
}
void delete_yourself()
{
if (self_)
self_.reset();
}
};
int main()
{
try
{
std::shared_ptr<missile> m = std::make_shared<missile>();
m->set_yourself();
std::weak_ptr<missile> wp = m;
std::cout << "before first reset()n";
m.reset();
std::cout << "after first reset()n";
// missile leaked here
m = wp.lock();
m->delete_yourself();
std::cout << "before second reset()n";
m.reset(); // missile deleted here
std::cout << "after second reset()n";
}
catch (const std::exception& e)
{
std::cout << e.what() << 'n';
}
}
我迟到了,但我现在碰到想自己做这件事,并意识到这是"可能的",但你需要照顾一些事情。
霍华德的答案是正确的,但错过了目标,因为你不应该把原始shared_ptr
的建设留给客户。这就是打开内存泄漏风险的原因。相反,您应该封装构造并只允许弱指针。
下面是一个示例:
class Missile{
private:
Missile(...){ }; // No external construction allowed
Missile(const Missile&) = delete; // Copying not allowed
void operator = (const Missile&) = delete; // -||-
std::shared_ptr<Missile> m_self;
public:
template<typename... Args>
static MissilePtr makeMissile(Args... args){
auto that = std::make_shared<Misile>(args...);
that.m_self = that; // that holds a reference to itself (ref count = 2)
return that; // 'that' is destroyed and ref-count reaches 1.
}
void die(){
m_self.reset();
}
...
};
typedef std::weak_ptr<Missile> MissilePtr;
void useMissile(MissilePtr ptr){
auto missile = ptr.lock(); // Now ptr cannot be deleted until missile goes out of scope
missile->die(); // m_self looses the reference but 'missile' still holds a reference
missile->whatever(); // Completely valid. Will not invoke UB
} // Exiting the scope will make the data in missile be deleted.
调用die()
将在语义上产生与delete this
相同的效果,但额外的好处是,引用已删除对象的所有MissilePtr
都将过期。此外,如果任何MissilePtr
用于访问this
则删除将被延迟,直到用于访问的临时std::shared_ptr
被销毁,从而为您带来终生的麻烦。
但是,您必须确保始终保持至少一个MissilePtr
,并在某个时候调用die()
否则您最终会出现内存泄漏。就像使用普通指针一样。
这个问题很老了,但我有一个类似的问题(在这种情况下,一个"侦听器"对象必须管理自己的生命周期,同时仍然能够共享弱指针),谷歌搜索并没有为我提供解决方案,所以我正在分享我找到的解决方案,假设:
- 对象管理自己的生命周期,因此永远不会共享share_ptr,但是一个weak_ptr(如果你需要shared_ptr是类似的解决方案+use_shared_from_this可以做到)。
- 打破 RAII 是一个坏主意,因此我们不会这样做:我们这里的地址是拥有对象拥有shared_ptr的问题本身,因为包含成员share_ptr会导致重复调用对象破坏和通常的崩溃(或至少未定义行为),因为析构函数被调用两次(一次在正常情况下)物体破坏和摧毁自我时的第二次破坏包含shared_ptr成员)。
法典:
#include <memory>
#include <stdio.h>
using std::shared_ptr;
using std::weak_ptr;
class A {
struct D {
bool deleted = false;
void operator()(A *p) {
printf("[deleter (%s)]n", p, deleted ? "ignored":"deleted");
if(!deleted) delete p;
}
};
public: shared_ptr<A> $ptr = shared_ptr<A>(this, D());
public: ~A() {
std::get_deleter<A::D>($ptr)->deleted = true;
}
public: weak_ptr<A> ptr() { return $ptr; }
};
void test() {
A a;
printf("count: %dn", a.ptr().lock().use_count());
printf("count: %dn", a.ptr().use_count());
}
int main(int argc, char *argv[]) {
puts("+++ main");
test();
puts("--- main");
}
输出:
$ g++ -std=c++11 -o test test.cpp && ./test
+++ main
count: 2
count: 1
[deleter (ignored)]
--- main
shared_ptr 删除器永远不应该为堆栈中分配的对象调用,所以当它在正常的对象销毁时,它只是绕过删除(我们走到这一步是因为已经调用了默认的对象析构函数)。
- 什么时候调用组成单元对象的析构函数
- 对RValue对象调用的LValue ref限定成员函数
- CMake-按正确顺序将项目与C运行时对象文件链接
- 空基优化子对象的地址
- 将对象数组的引用传递给函数
- 你能重载对象变量名本身返回的内容吗
- C++使用整数的压缩数组初始化对象
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 将对象移动到std::shared_ptr
- 代理对象的常量正确性
- 提升 ASIO 无法识别计时器对象
- 将Ref对象作为类成员
- 将包含C样式数组的对象初始化为成员变量(C++)
- 如何返回一个类的两个对象相加的结果
- 使用std::函数映射对象方法
- 是否需要删除包含对象的"pair"?
- 如何在自删除后将对象设置为nullptr
- 迭代时从向量和内存中删除对象
- 将GoogleMock与Boost::Shared Pointers一起使用时泄漏的Mock对象
- 在python中打开共享对象文件时出错(OSError: cannot open shared object file: