共享状态不必强制使用堆内存,但栈对象因作用域限制易导致悬空指针,故安全共享几乎总依赖堆内存与shared_ptr等智能指针管理;其通过引用计数自动协调生命周期,避免泄漏或提前释放。

共享状态必须用堆内存分配的指针吗?
不是必须,但栈上对象的生命周期受限于作用域,一旦离开作用域,指向它的指针就变成悬空指针。所以真正安全的共享,几乎总是依赖堆内存(new 或智能指针管理的对象)。裸指针本身不管理生命周期,只传递地址;若多个对象持有同一裸指针却无人负责 delete,就会泄漏;若有人提前 delete,其余指针立刻失效。
实操建议:
- 优先用
std::shared_ptr<t></t>,它通过引用计数自动管理堆对象生命周期,适合多对象读写同一状态 - 若状态只读或由单一所有者控制写入,可用
std::weak_ptr<t></t>配合shared_ptr防止循环引用 - 避免裸指针(
T*)跨对象传递——除非你明确知道谁构造、谁销毁,且能保证调用顺序 - 不要把局部变量地址(如
&local_obj)赋给其他对象的指针成员
如何让多个类实例共享一个 shared_ptr 成员?
关键在于:所有对象都持有一个指向同一堆对象的 shared_ptr 拷贝,而非各自 new 一份。拷贝 shared_ptr 会自动增加引用计数,析构时自动减计数,仅当计数归零才释放资源。
示例场景:两个 Worker 对象共享一个计数器 Counter:
立即学习“C++免费学习笔记(深入)”;
struct Counter { int value = 0; };
struct Worker {
std::shared_ptr<Counter> counter;
Worker(std::shared_ptr<Counter> c) : counter(c) {}
void inc() { ++counter->value; }
};
auto shared_counter = std::make_shared<Counter>();
Worker w1(shared_counter);
Worker w2(shared_counter);
w1.inc(); w2.inc();
// 此时 shared_counter->value == 2
注意:std::make_shared 比 new + shared_ptr 构造更高效(内存一次分配);直接传值(非引用)初始化成员是安全的,因为 shared_ptr 拷贝是轻量级操作。
裸指针共享状态时最常见的崩溃原因
90% 的问题来自生命周期错配:某个对象 delete 了共享数据,其他对象还在用。典型错误现象包括:
- 程序在访问指针成员时触发
EXC_BAD_ACCESS(macOS)或Access violation(Windows) - 值突然变成随机大数或零(未初始化内存被复用)
- 多线程下偶发崩溃,单线程正常(实际是竞态导致释放时机不可控)
排查思路:
- 检查所有可能 delete 该指针的地方,确认是否有多处释放(double-delete)
- 用 AddressSanitizer(ASan)编译运行,它能直接报出悬空指针访问位置
- 把裸指针换成
shared_ptr后问题消失,基本可锁定为生命周期管理缺失 - 避免在析构函数里调用其他对象的回调,回调中可能间接使用已析构对象的指针
共享状态需要线程安全吗?怎么加锁?
如果多个线程可能同时读写同一块共享状态(比如多个 Worker 并发调用 inc()),就必须同步。仅读可以无锁,但“读-改-写”(如 ++counter->value)一定需要原子操作或互斥。
推荐做法:
- 对简单整数/布尔等,优先用
std::atomic<T>,性能好且无锁(如std::atomic_int) - 对复杂结构(如容器、自定义类),用
std::mutex+std::lock_guard包裹临界区 - 不要在持有锁时调用外部函数(尤其是虚函数或回调),防止死锁或异常导致锁未释放
- 若用
shared_ptr管理状态,注意shared_ptr本身的引用计数是线程安全的,但其指向的对象内容不是
例如,把上面的 Counter 改为线程安全:
struct Counter {
std::atomic_int value{0};
void inc() { ++value; }
};
共享状态最难的从来不是“怎么传指针”,而是“谁决定它什么时候消失”和“谁保证并发时不打架”。这两个问题没想清楚前,任何指针方案都只是延缓崩溃时间。


















