有一些相关问题,例如smart pointers + "this" considered harmful?,但它们不能解决我的问题。所有这些问题都是关于在引用计数智能指针的情况下暴露原始this指针。但是,我的问题并不在于我公开this
,而是仅在方法中使用它,但可能需要更长的时间。
请考虑以下代码:
class X{
void foo(){...} //Performs some long running task
}
shared_ptr<X> x;
void bar(){
x->foo();
}
好的,所以有些代码调用了对象x的foo
方法。考虑到x后面的实例的唯一智能引用是shared_ptr x。现在,foo执行一些长时间运行的任务,调用其他函数和东西。
现在,考虑一下,当foo正在运行时,foo
中的另一个线程,信号处理程序甚至递归调用会更改或清除引用x
。这将触发删除对象。现在,this
的调用堆栈上的foo
指针指向已删除的对象。因此foo
中的进一步代码执行将产生不可预测的结果。
如何避免这种情况?我的意思是,每当执行通过引用计数处理的对象的方法时,存在某些代码可能清除对对象的引用并且方法调用将失败或产生奇怪结果的危险。问题是语义:引用计数认为没有更多对该对象的引用并因此删除它,但事实并非如此 - 仍然存在this
引用,但遗憾的是,它不是一个智能指针
我知道有enable_shared_from_this
这样的东西,但是我应该总是重写所有可能被引用计数的对象方法,首先从this
获取共享指针,然后使用该指针,除了这个问题?这会使所有方法代码混乱,此外,它可能仍会失败:如果事件在调用foo之后但在方法中的第一个语句(获取共享实例)执行之前清除x
(也许是另一个线程),然后事情会再次失败。
所以一般的问题是:
使用任何类型的智能指针时,如何在对托管对象的方法调用期间保证安全?如何确保方法内的this
指针不会变为无效?在第一个语句中重写所有使用enable_shared_from_this
的方法是一个好的解决方案吗?
或者是代码清除引用,而该引用仍然在调用堆栈上,只是格式不正确?但如果是,那么一旦使用多个线程和复杂的递归调用就很难编写非错误的代码...
答案 0 :(得分:1)
关于多个线程:在线程之间传递数据时,应始终通过 copy 传递共享指针,而不是通过引用传递。此规则将确保线程不会丢失另一个线程清除它具有引用的共享指针。
关于递归方法调用:是的,您应该规定,每当您通过共享指针调用方法时,可能会导致其他共享指针被清除,您就有了副本本地共享指针的如果按值将共享指针传递给函数,这通常不会成为问题。
我能看到的唯一一个问题就是代码中出现x
是类数据成员:
class C {
shared_ptr<X> x;
public:
void method1() { x.reset; }
void method2() {
x->foo();
}
};
在这种情况下,如果可以想象method1
可以从X::foo
调用,则必须使用x
的本地副本。当您在类中存储共享指针时,这只是 一个问题,并且仅影响该类上的方法。
答案 1 :(得分:0)
分享是关怀:获取变量的新份额!
shared_ptr<X> x;
void bar()
{
shared_ptr<X> my_x = x;
my_x->foo();
}