std::shared_ptr 和 std::unique_ptr 的 if(ptr) 依赖重载的 operator bool(),返回 get() != nullptr,仅检查原始指针是否为空,不保证资源有效;多线程下需额外同步,if(ptr) 无法防止悬空解引用。

std::shared_ptr 和 std::unique_ptr 的 if(ptr) 是怎么工作的
它们都重载了 operator bool(),不是隐式转 void* 或指针类型。这个转换函数返回 get() != nullptr,也就是只看内部原始指针是否为空,不关心引用计数是否为 0(哪怕只剩最后一个 owner,只要没被 reset,if(ptr) 仍为 true)。
常见错误现象:if(ptr) 通过,但 *ptr 崩溃——这说明 ptr 指向的资源已被释放(比如多线程中另一个线程刚调用了 reset()),但智能指针对象本身还没被析构,get() 还非空(悬空指针)。这时候 if(ptr) 完全无法预防崩溃。
- 别把
if(ptr)当成“资源还活着”的保证,它只表示“我目前还管着一个非空地址” - 多线程下必须配合
std::atomic<:shared_ptr>></:shared_ptr>或外部锁,不能靠if(ptr)同步生命周期 -
std::weak_ptr不提供operator bool(),必须先lock()得到shared_ptr才能判空
std::weak_ptr 怎么安全判断是否过期
weak_ptr 没有 operator bool(),直接写 if(wp) 会编译失败。它的生命周期独立于所观察的对象,所以“是否为空”和“是否过期”是两个问题:前者指 wp 自身没被构造或已 reset(wp.expired() 返回 true),后者指它曾指向的对象已被销毁。
正确做法永远是先 lock(),再判断返回的 shared_ptr 是否有效:
立即学习“C++免费学习笔记(深入)”;
auto sp = wp.lock();
if (sp) {
// 对象还活着,sp 是合法的 shared_ptr
use(*sp);
}
-
wp.expired()等价于wp.lock().get() == nullptr,但多一次原子操作,不如直接lock()+ 判空简洁 - 不要用
wp.use_count()判断——它可能因竞争而立即失效,且use_count()在多线程下不是原子读(C++20 起才对shared_ptr的use_count加了原子保证,但weak_ptr::use_count依然没有) - 如果只是想避免
lock()的开销,又确定无并发修改,可先if (!wp.expired())再lock(),但绝大多数场景直接lock()更安全
自定义删除器会影响 if(ptr) 的行为吗
完全不影响。if(ptr) 只依赖 get() 返回值,而 get() 返回的是原始指针,跟删除器逻辑、是否已执行删除器、删除器有没有抛异常都无关。即使你写了会立即释放内存的删除器,只要 reset() 还没被调用,get() 就仍返回原地址。
- 删除器只在智能指针析构或
reset()时触发,if(ptr)不触发任何资源管理动作 - 如果删除器里手动把指针置为
nullptr(比如包装了 C API 的 free 函数后又清了字段),那get()仍返回旧值——因为智能指针内部不感知你的删除器改了什么 - 真正危险的是:删除器抛异常 +
shared_ptr在栈上析构 → 异常传播导致未定义行为;但这和if(ptr)无关
为什么 raw pointer 也能 if(p),但和智能指针语义不同
裸指针的 if(p) 是隐式转换为 bool(C++11 起禁止转 void*,但允许转 bool),本质就是 p != nullptr。表面一致,但背后毫无保障:它不表达所有权,不控制生命周期,也不防重复释放。
- 混用裸指针和智能指针时,千万别用
if(p)去“代替”智能指针的空检查——比如把shared_ptr的get()结果存成裸指针再判空,那就彻底失去 RAII 保护 - Clang/GCC 开启
-Wdangling-gsl或使用 GSL 的not_null<t></t>可在编译期捕获部分裸指针误用,但无法替代智能指针的运行时语义 - 最隐蔽的坑:有人写
if (ptr.get()),以为比if(ptr)更“明确”,其实多余,且弱化了类型意图——编译器不会帮你发现你本该用shared_ptr却用了裸指针
真正容易被忽略的点是:所有这些判空操作,都不涉及引用计数访问(shared_ptr::use_count() 才会),所以性能几乎为零;但它们也完全不提供线程安全保证——判空和后续使用之间存在天然竞态窗口,这点连 weak_ptr::lock() 都只能缓解,不能根除。









