对象构造未完成就被其他线程访问会导致段错误、垃圾值、std::terminate崩溃或成员未初始化等未定义行为,根本原因是构造函数未结束时this指针被暴露;安全做法是严格禁止在构造函数中启动线程、注册回调或存入全局容器,必须待构造完成后再通过工厂函数或start()等显式方法发布对象。

对象构造未完成就被其他线程访问的典型现象
这种情况通常表现为段错误、读到垃圾值、std::terminate 崩溃,或者更隐蔽地——成员变量为零初始化但本该被构造函数赋值,导致逻辑错乱。根本原因是:**构造函数还没执行完,this 指针已暴露给其他线程**(比如在构造函数里启动线程、注册回调、放入全局容器等)。
如何安全地发布 this 指针
核心原则:**绝不在构造函数中做任何可能让 this 被其他线程看到的操作**。常见错误包括:
- 在构造函数中调用
std::thread并传入this或成员函数 - 把
this存入全局std::vector<:shared_ptr>></:shared_ptr>或静态 map - 调用虚函数(尤其是虚析构函数尚未就绪时,动态绑定可能出问题)
- 将
this传给异步回调(如std::async、第三方 event loop)
正确做法是:构造完成后再“发布”。例如用工厂函数封装:
class Worker {
public:
Worker() { /* 只做基础初始化,不发 this */ }
void start() { /* 启动线程、注册等放这里 */ }
};
<p>auto ptr = std::make_shared<Worker>();
ptr->start(); // 安全:构造已完成
std::shared_ptr 的陷阱:构造中 make_shared 不等于安全
std::make_shared 确保内存分配和构造原子,但它**不阻止你在构造函数里主动泄露 this**。下面这段代码依然危险:
立即学习“C++免费学习笔记(深入)”;
struct Bad {
Bad() {
// 危险!此时构造函数还没返回,对象未完全就绪
global_list.push_back(this); // this 暴露了
}
};
即使你用 std::make_shared<Bad>() 创建它,global_list 里的指针仍指向一个部分构造的对象。真正安全的发布时机必须是构造函数**正常返回之后**。
如果必须在构造阶段启动线程怎么办
极少场景下(如 RAII 封装的线程池资源),需要“构造即运行”,这时唯一可靠方式是:**延迟线程实际工作,直到对象完全构造完毕**。常用技巧:
- 用
std::atomic_bool ready{false}标记构造完成状态 - 线程入口函数开头加
while (!ready.load()) std::this_thread::yield(); - 在构造函数末尾才设置
ready.store(true) - 确保
ready是对象内成员,且不依赖其他未构造成员
注意:这增加了同步开销,且 yield 不是实时保证;更推荐重构为两阶段初始化(如上文 start() 方式)。
最常被忽略的一点:即使你没显式传 this,某些库的注册接口(比如 Qt 的 connect、Boost.Asio 的 post)内部可能捕获并跨线程使用对象指针——只要发生在构造函数中,就属于未定义行为。


















