如何在类成员协程中安全访问this指针?
用C++标准库解决协程中对象提前销毁的问题
要避免协程访问已销毁对象的未定义行为,核心是让协程弱引用对象,在需要访问成员前先确认对象是否存活。借助C++标准库的std::weak_ptr和std::enable_shared_from_this就能实现,具体方案如下:
步骤1:让类继承std::enable_shared_from_this
通过继承这个模板类,对象可以安全获取自身的弱引用,避免手动创建引用导致的计数错误。
步骤2:在协程中持有弱引用并验证对象存活
协程启动时先捕获自身的弱引用,挂起恢复后尝试将弱引用升级为强引用:
- 升级成功:对象仍存活,安全访问成员变量
- 升级失败:对象已销毁,执行错误处理逻辑
修改后的完整代码
#include <iostream> #include <memory> #include <stdexcept> #include <chrono> #include <coroutine> // 符合C++20协程标准的Task实现(示例用) template<typename T> struct Task { struct promise_type { T value; std::suspend_never initial_suspend() { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void unhandled_exception() { std::terminate(); } Task get_return_object() { return Task{this}; } void return_value(T v) { value = std::move(v); } }; promise_type* promise; explicit Task(promise_type* p) : promise(p) {} T get() { return std::move(promise->value); } }; struct MyClass : std::enable_shared_from_this<MyClass> { std::string m_value{ "Hello, World!" }; Task<std::string> RetrieveValueAsync() { // 获取自身的弱引用,不增加引用计数 auto weak_this = weak_from_this(); co_await std::chrono::seconds(5); // 挂起后在线程池恢复 // 尝试升级为强引用,确认对象是否存活 if (auto strong_this = weak_this.lock()) { co_return strong_this->m_value; } else { // 对象已销毁,抛出异常或返回默认值 throw std::runtime_error("对象已在协程完成前被销毁"); } } }; int main() { try { auto instance = std::make_shared<MyClass>(); auto async_task = instance->RetrieveValueAsync(); instance = nullptr; // 模拟对象提前销毁 std::string result = async_task.get(); std::cout << result << std::endl; } catch (const std::exception& e) { std::cerr << "错误:" << e.what() << std::endl; } }
关键细节说明
- 弱引用不阻止销毁:协程持有
std::weak_ptr不会增加对象的引用计数,因此对象可以正常被销毁(比如main中instance = nullptr时)。 - 线程安全的存活验证:
weak_ptr::lock()是线程安全的操作,能可靠判断对象是否还存在,避免悬空指针访问。 - 错误处理灵活:对象销毁时可以选择抛出异常、返回默认值,或者根据业务需求执行其他逻辑,完全替代C++/WinRT中弱引用的作用。
内容的提问来源于stack exchange,提问作者Tom Huntington
相关产品推荐
相关产品推荐

