提问人:Ryan 提问时间:10/11/2023 最后编辑:Ryan 更新时间:10/20/2023 访问量:145
如何检查类的“this”指针在回调函数中是否仍然有效?
How could I check if "this" pointer of a class is still valid in a callback function?
问:
class myClass
{
public:
void fun()
{
async_do( [this](int j)
{
i+=j;
} );
}
private:
int i=9;
}
int main()
{
std::shared_ptr<myClass> obj = std::make_shared<myClass>();
obj->fun();
obj->reset();
return 0;
}
上面的代码,当我们调用时,它会调用,会立即返回。异步操作完成后,将调用 lambda 函数。但是,当此异步操作仍在运行时,对象可能会被销毁。调用 lambda 时,该对象不再存在。在这种情况下,我应该在这个回调函数中做什么?
谢谢你的帮助。fun()
async_do()
async_do()
答:
2赞
Louis Go
10/11/2023
#1
用例:当您需要创建回调,但回调的调用者不知道回调的生存期,并且设计允许在调用回调之前进行 destructed。this
this
两者的区别在于,它不会使实例保持活动状态,而只是在需要时才尝试访问它。需要注意的是,捕获可能会延长实例的生命周期,直到释放回调。它可能不适合所有用例。shared_from_this()
weak_from_this()
std::shared_ptr
例如: 只需要调用最新的实例,而不是保留所有实例,并且不想保留其他实例。
#include <fmt/core.h>
#include <fmt/format.h>
#include <functional>
#include <memory>
using Callback = std::function<void(int)>;
class myClass : public std::enable_shared_from_this<myClass>
{
public:
Callback createWeakCallback()
{
return [weak_ref = weak_from_this()](int j) {
auto strong_ref = weak_ref.lock();
if (nullptr == strong_ref){
fmt::print("This is dead.\n");
return;
}
// use `strong_ref` as `this`
strong_ref->add(j);
};
}
Callback createStrongCallback()
{
return [shared = shared_from_this()](int j) {
shared->add(j);
};
}
myClass(int val): i(val){}
~myClass(){
fmt::print("dtor, i:{}\n", i);
}
void add(int j){
i+=j;
fmt::print("i: {}\n", i);
}
private:
int i=9;
};
int main()
{
std::shared_ptr<myClass> obj = std::make_shared<myClass>(0);
Callback cb = obj->createWeakCallback();
cb(6);
obj.reset();
cb(5);
{ // Scope of strongCb
Callback strongCb;
{
std::shared_ptr<myClass> obj = std::make_shared<myClass>(100);
strongCb = obj->createStrongCallback();
strongCb(10); // <-- still alive
fmt::print("End of obj first std::shraed_ptr scope\n");
}
fmt::print("End of strongCb\n");
} // <-- obj get destructed here
return 0;
}
输出:
i: 6
dtor, i:6
This is dead.
i: 110
End of obj first std::shraed_ptr scope
End of strongCb
dtor, i:110
0赞
Jorge
10/11/2023
#2
您可以捕获对象而不是原始指针,以确保在调用 lambda 回调时对象仍处于活动状态。回调将保存对对象的引用,确保在回调完成之前不会销毁对象。std::shared_ptr
#include <iostream>
#include <memory>
#include <functional>
void async_do(std::function<void(int)> callback) {
// async callback
}
class myClass : public std::enable_shared_from_this<myClass>
{
public:
void fun()
{
auto self = shared_from_this();
async_do([self](int j) {
self->i += j;
});
}
private:
int i = 9;
};
int main()
{
std::shared_ptr<myClass> obj = std::make_shared<myClass>();
obj->fun();
obj.reset(); // This will not destroy the object if the callback hasn't finished yet
return 0;
}
如果你不能使用智能指针,我建议你实现一个最低限度工作的共享指针。方法应该是类似的。
您还可以通过为 myClass 的每个实例分配一个唯一的 ID 来使用基于令牌的系统。然后,使用静态映射来保存对类的活动实例的引用,并在调用 fun() 时,为异步操作提供 ID。在回调中,检查 ID 是否仍然存在。如果是这样,则该对象仍处于活动状态,您可以继续。下面是一个最小的示例:
#include <iostream>
#include <functional>
#include <unordered_map>
void async_do(std::function<void(int)> callback) {
// ...
}
class myClass {
public:
myClass() {
id = ++lastId;
activeInstances[id] = this;
}
~myClass() {
activeInstances.erase(id);
}
void fun() {
int localId = id;
async_do([localId](int j) {
if (activeInstances.count(localId)) {
activeInstances.at(localId) += j;
}
});
}
private:
int i = 9;
int id;
static int lastId;
static std::unordered_map<int, myClass*> activeInstances;
};
int myClass::lastId = 0;
std::unordered_map<int, myClass*> myClass::activeInstances;
int main() {
{
myClass obj;
obj.fun();
// obj will be destroyed at the end of this scope
}
// ...
return 0;
}
评论
enable_shared_from_this
?this
shared_ptr
this
weak_from_this
的地方。