C++中含thread对象的类如何实现移动构造与赋值?
解决MyClass的移动构造与移动赋值问题
首先得明确:直接移动thread成员是行不通的——因为线程在构造时捕获的是旧对象的this指针,就算把线程所有权转移到新对象,它还是会一直操作旧对象的a成员,完全达不到我们想要的效果。
要让线程在移动后转而操作新对象的成员,我们需要给线程加一个"动态指向"的入口,不能让它绑定死固定的this。下面是完整的正确实现:
#include <atomic> #include <thread> #include <chrono> using namespace std::chrono_literals; class MyClass { private: std::atomic_int a; std::thread t; std::atomic<MyClass*> self; // 新增:让线程通过这个指针访问当前对象 public: // 默认构造函数 MyClass() : a(0), self(this), t([this]() { while (true) { std::this_thread::sleep_for(1000ms); // 通过self指针访问成员,而不是直接用this if (auto current_obj = self.load()) { current_obj->a++; } else { break; // self为空时,线程安全退出 } } }) {} // 移动构造函数 MyClass(MyClass&& other) noexcept : a(std::move(other.a)), t(std::move(other.t)), self(this) { // 让旧对象的线程转而指向新对象 other.self.store(this); } // 移动赋值运算符 MyClass& operator=(MyClass&& other) noexcept { if (this != &other) { // 先处理当前对象的线程:让它退出并等待结束 self.store(nullptr); if (t.joinable()) { t.join(); } // 移动成员变量 a = std::move(other.a); t = std::move(other.t); // 让旧对象的线程现在指向当前新对象 other.self.store(this); self.store(this); } return *this; } // 析构函数:确保线程安全退出 ~MyClass() { self.store(nullptr); if (t.joinable()) { t.join(); } } // 禁止拷贝(thread和atomic都不适合拷贝) MyClass(const MyClass&) = delete; MyClass& operator=(const MyClass&) = delete; // 可选:提供获取a的接口,方便测试 int get_a() const { return a.load(); } };
关键细节解释
self原子指针:这是核心——线程不再直接使用构造时捕获的this,而是通过这个可动态更新的原子指针访问目标对象的a。原子操作保证了多线程环境下的访问安全。- 移动构造逻辑:
- 先移动
a和thread的所有权到新对象; - 更新旧对象的
self指针指向新对象,这样原来的线程就会开始操作新对象的a;
- 先移动
- 移动赋值逻辑:
- 首先要处理当前对象的线程:把
self置空,让线程退出,然后join等待线程结束,避免资源泄漏; - 再移动成员变量,最后更新旧对象的
self指向当前对象;
- 首先要处理当前对象的线程:把
- 析构函数:通过把
self置空,让线程安全退出,然后join线程,防止程序退出时线程还在运行导致未定义行为。
这样实现后,移动操作完成后,线程会完全属于新对象,并正确操作新对象的a成员。
内容的提问来源于stack exchange,提问作者haael
相关产品推荐
相关产品推荐

