C++标准不提供垃圾回收,引用计数(如std::shared_ptr)仅为RAII式确定性释放,并非真正GC;它无法解决循环引用、无堆扫描能力、有性能开销且不管理非new内存或非内存资源。

C++ 标准本身不提供垃圾回收(GC),但可以用引用计数实现类 GC 的自动内存管理效果——关键在于:它不是真正的 GC,而是确定性、可预测的资源释放;用错场景反而会引发循环引用或性能问题。
为什么不能直接用 std::shared_ptr 当“垃圾回收器”
std::shared_ptr 是引用计数智能指针,但它不解决循环引用,也不扫描堆内存。它只是在引用数归零时立即调用 delete,属于 RAII 范畴,不是 GC。
- 循环引用时,两个对象互相持有
shared_ptr,引用计数永不为 0,内存泄漏 - 频繁拷贝
shared_ptr会带来原子操作开销(尤其在多线程下) - 无法回收原始裸指针、
malloc分配的内存、或非new构造的对象 - 不处理栈对象、全局对象、或文件句柄等非内存资源(除非你自定义 deleter)
手动实现轻量引用计数类的关键点
自己写一个最小可用的引用计数指针,能帮你理解底层逻辑,也便于定制行为(比如日志、调试计数、弱引用支持)。
templateclass RefCountPtr { T* ptr_; size_t* count_; void release() { if (count_ && --(*count_) == 0) { delete ptr_; delete count_; } }public: explicit RefCountPtr(T* p = nullptr) : ptr(p), count(p ? new size_t(1) : nullptr) {}
RefCountPtr(const RefCountPtr& other) : ptr_(other.ptr_), count_(other.count_) { if (count_) ++(*count_); } RefCountPtr& operator=(const RefCountPtr& other) { if (this != &other) { release(); ptr_ = other.ptr_; count_ = other.count_; if (count_) ++(*count_); } return *this; } ~RefCountPtr() { release(); } T& operator*() const { return *ptr_; } T* operator-youjiankuohaophpcn() const { return ptr_; }};
立即学习“C++免费学习笔记(深入)”;
- 必须用
size_t*单独分配计数器,不能和对象布局耦合(否则无法通用)- 拷贝构造/赋值中要判空,避免对
nullptr解引用或递增空指针- 析构时只负责释放计数器和对象,不负责重置
ptr_(这是安全的,因对象已销毁)- 没实现
weak_ptr等价物,所以仍无法破循环引用如何检测和打破循环引用
循环引用不是语法错误,运行期才暴露为内存泄漏。靠工具或设计约束来预防更实际。
- 用
std::weak_ptr替代部分shared_ptr:比如观察者模式中,被观察者持shared_ptr,而观察者内部用weak_ptr回调- 静态分析:Clang 的
-Wlifetime可捕获部分悬垂引用,但对跨对象循环无能为力- 运行时调试:重载
operator new+ 全局计数器,配合atexit()检查未释放对象数量- 设计上规避:用 ID 或句柄(
int)代替裸指针/智能指针传递,由中心容器管理生命周期什么场景下真该考虑外部 GC(如 Boehm GC)
只有当你明确需要“不可预测但自动”的回收语义,且能接受限制时,才引入第三方 GC。
- 嵌入脚本解释器(如用 C++ 实现 Lua host),需托管脚本对象生命周期
- 遗留代码大量使用
malloc/free,又不愿重构为 RAII- 算法原型阶段,优先保证逻辑正确,暂不优化内存路径
Boehm GC 是保守式 GC,它可能把栈上一个整数误认为指针,从而漏回收真实对象;它不调用析构函数,所以不能用于管理含资源(文件、锁、GPU buffer)的对象——这点常被忽略。










