调用 std::function 对象指向释放对象的方法
Calling std::function object pointing to the method of deallocated object
考虑这段代码:
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
typedef function<void(const int&)> SomeFunc;
class X {
public:
X(string name):name_(name)
{ cout << "ctor " << name_ << endl; }
~X()
{
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc
getSomeFunc()
{ return bind(&X::someMethod, this, _1); }
private:
string name_;
void
someMethod(const int& a)
{
cout << name_ << " some method with " << a << endl;
}
};
int main()
{
SomeFunc f;
{
shared_ptr<X> x(new X("Object"));
f = x->getSomeFunc();
f(1);
}
f(2);
return 0;
}
有时,输出会给我这个:
ctor Object
Object some method with 1
dtor Object
empty some method with 2
其他时候是这样的:
ctor Object
Object some method with 1
dtor Object
some method with 2
在现实世界中,一旦解除分配的对象试图访问它的属性,它很可能会让我崩溃。
所以这是一个问题 - 由于函数不保证持有对对象的引用,它指向哪个方法,在引用的对象已经被释放后调用函数时避免崩溃的最佳实践是什么?
我可能想到的解决方案之一 - 在对象中维护一个特殊标志 bool deallocated_
并在释放后可能调用的方法中检查它。但是,我怀疑,它也不可靠。
更新(来自评论):
我需要此解决方法的真正原因是将函数作为参数的库。这个库异步运行,我无法控制传递给它的函数对象。这就是为什么当我的对象被释放时,库仍然可以使用最初传递的函数调用回调,这会导致崩溃。
您可以创建一个包含函数指针的 class 和指向该对象的 shared_ptr
。对象的 shared_ptr
保证在你的函数 class 被销毁之前对象不会被销毁。
您的对象由 shared_ptr
持有,因此您可以使用 lambda 来关闭 shared_ptr
:
auto func = [ptr](const int &p){ ptr->someMethod(p); };
您需要使用 shared_from_this
才能在 class 内获得 ptr
。
这是一个完整的有效示例:
#include <iostream>
#include <functional>
#include <memory>
using namespace std;
using namespace std::placeholders;
typedef function<void(const int&)> SomeFunc;
class X : public enable_shared_from_this<X> {
public:
X(string name) : name_(name) {
cout << "ctor " << name_ << endl;
}
~X() {
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc getSomeFunc() {
auto ptr = shared_from_this();
return [ptr](const int &a){ ptr->someMethod(a); };
}
private:
string name_;
void someMethod(const int& a) {
cout << name_ << " some method with " << a << endl;
}
};
int main()
{
SomeFunc f;
{
shared_ptr<X> x(new X("Object"));
f = x->getSomeFunc();
f(1);
}
f(2);
return 0;
}
输出如下所示:
ctor Object
Object some method with 1
Object some method with 2
dtor Object
解决方案 1) 使用 weak_ptr + lambda(与 b4hand 几乎相同,但不会强制你的 class 还活着)
从std::enable_shared_from_this
继承你的class
class X : public enable_shared_from_this<X>
并将 getSomeFunc 更改为如下内容:
SomeFunc getSomeFunc()
{
weak_ptr<X> weak = shared_from_this();
return [weak, this](const int& a){
shared_ptr<X> shared = weak.lock();
if (shared)
{
this->someMethod(a);
}
};
}
输出:
ctor Object
Object some method with 1
dtor Object
更多详情here and here。
解决方案 2) 有点疯狂的代码 + lambda
如果你不能或不想使用 shared/weak 点,你可以这样做:
#include <memory>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <set>
using namespace std;
typedef function<void(const int&)> SomeFunc;
class X {
private:
static set<X*> _aliveInstanties;
public:
X(string name) :name_(name)
{
_aliveInstanties.insert(this);
cout << "ctor " << name_ << endl;
}
~X()
{
_aliveInstanties.erase(_aliveInstanties.find(this));
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc getSomeFunc()
{
return [this](const int& a)
{
if (_aliveInstanties.find(this) != _aliveInstanties.end())
{
this->someMethod(a);
}
};
}
private:
string name_;
void someMethod(const int& a)
{
cout << name_ << " some method with " << a << endl;
}
};
另一种不使用 lambda 的解决方案是从 enable_shared_from_this
派生并在 getSomeFunc
方法中传递 shared_from_this
:
class X : public enable_shared_from_this<X> {
public:
X(string name):name_(name)
{ cout << "ctor " << name_ << endl; }
~X()
{
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc
getSomeFunc()
{
return bind(&X::someMethod, shared_from_this(), _1);
}
private:
string name_;
void
someMethod(const int& a)
{
cout << name_ << " some method with " << a << endl;
}
};
然而,这将保留对象,直到释放所有回调。
考虑这段代码:
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
typedef function<void(const int&)> SomeFunc;
class X {
public:
X(string name):name_(name)
{ cout << "ctor " << name_ << endl; }
~X()
{
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc
getSomeFunc()
{ return bind(&X::someMethod, this, _1); }
private:
string name_;
void
someMethod(const int& a)
{
cout << name_ << " some method with " << a << endl;
}
};
int main()
{
SomeFunc f;
{
shared_ptr<X> x(new X("Object"));
f = x->getSomeFunc();
f(1);
}
f(2);
return 0;
}
有时,输出会给我这个:
ctor Object
Object some method with 1
dtor Object
empty some method with 2
其他时候是这样的:
ctor Object
Object some method with 1
dtor Object
some method with 2
在现实世界中,一旦解除分配的对象试图访问它的属性,它很可能会让我崩溃。 所以这是一个问题 - 由于函数不保证持有对对象的引用,它指向哪个方法,在引用的对象已经被释放后调用函数时避免崩溃的最佳实践是什么?
我可能想到的解决方案之一 - 在对象中维护一个特殊标志 bool deallocated_
并在释放后可能调用的方法中检查它。但是,我怀疑,它也不可靠。
更新(来自评论):
我需要此解决方法的真正原因是将函数作为参数的库。这个库异步运行,我无法控制传递给它的函数对象。这就是为什么当我的对象被释放时,库仍然可以使用最初传递的函数调用回调,这会导致崩溃。
您可以创建一个包含函数指针的 class 和指向该对象的 shared_ptr
。对象的 shared_ptr
保证在你的函数 class 被销毁之前对象不会被销毁。
您的对象由 shared_ptr
持有,因此您可以使用 lambda 来关闭 shared_ptr
:
auto func = [ptr](const int &p){ ptr->someMethod(p); };
您需要使用 shared_from_this
才能在 class 内获得 ptr
。
这是一个完整的有效示例:
#include <iostream>
#include <functional>
#include <memory>
using namespace std;
using namespace std::placeholders;
typedef function<void(const int&)> SomeFunc;
class X : public enable_shared_from_this<X> {
public:
X(string name) : name_(name) {
cout << "ctor " << name_ << endl;
}
~X() {
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc getSomeFunc() {
auto ptr = shared_from_this();
return [ptr](const int &a){ ptr->someMethod(a); };
}
private:
string name_;
void someMethod(const int& a) {
cout << name_ << " some method with " << a << endl;
}
};
int main()
{
SomeFunc f;
{
shared_ptr<X> x(new X("Object"));
f = x->getSomeFunc();
f(1);
}
f(2);
return 0;
}
输出如下所示:
ctor Object
Object some method with 1
Object some method with 2
dtor Object
解决方案 1) 使用 weak_ptr + lambda(与 b4hand 几乎相同,但不会强制你的 class 还活着)
从std::enable_shared_from_this
继承你的classclass X : public enable_shared_from_this<X>
并将 getSomeFunc 更改为如下内容:
SomeFunc getSomeFunc()
{
weak_ptr<X> weak = shared_from_this();
return [weak, this](const int& a){
shared_ptr<X> shared = weak.lock();
if (shared)
{
this->someMethod(a);
}
};
}
输出:
ctor Object
Object some method with 1
dtor Object
更多详情here and here。
解决方案 2) 有点疯狂的代码 + lambda
如果你不能或不想使用 shared/weak 点,你可以这样做:
#include <memory>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <set>
using namespace std;
typedef function<void(const int&)> SomeFunc;
class X {
private:
static set<X*> _aliveInstanties;
public:
X(string name) :name_(name)
{
_aliveInstanties.insert(this);
cout << "ctor " << name_ << endl;
}
~X()
{
_aliveInstanties.erase(_aliveInstanties.find(this));
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc getSomeFunc()
{
return [this](const int& a)
{
if (_aliveInstanties.find(this) != _aliveInstanties.end())
{
this->someMethod(a);
}
};
}
private:
string name_;
void someMethod(const int& a)
{
cout << name_ << " some method with " << a << endl;
}
};
另一种不使用 lambda 的解决方案是从 enable_shared_from_this
派生并在 getSomeFunc
方法中传递 shared_from_this
:
class X : public enable_shared_from_this<X> {
public:
X(string name):name_(name)
{ cout << "ctor " << name_ << endl; }
~X()
{
cout << "dtor " << name_ << endl;
name_ = "empty";
}
SomeFunc
getSomeFunc()
{
return bind(&X::someMethod, shared_from_this(), _1);
}
private:
string name_;
void
someMethod(const int& a)
{
cout << name_ << " some method with " << a << endl;
}
};
然而,这将保留对象,直到释放所有回调。