是的,在现代 C++(C++11 及以后)开发中,过度使用 std::shared_ptr 确实被许多专家和社区视为一种“代码恶臭”(code smell),甚至被比作“穷人的垃圾回收”(poor man’s GC)或“隐形的全局变量”。
为什么过度使用 shared_ptr 被视为问题?
shared_ptr 的核心是共享所有权(shared ownership)和引用计数机制,这带来了以下缺点:
- 性能开销:引用计数需要原子操作(线程安全),拷贝/析构时有额外代价。相比
unique_ptr或普通引用/指针,慢得多,尤其在高频操作或实时系统中。 - 内存开销:每个
shared_ptr需要额外控制块(引用计数 + 删除器),通常是两个指针大小(16 字节 + 控制块)。 - 循环引用风险:容易导致内存泄漏(需用
weak_ptr打破循环,但这增加了复杂性)。 - 所有权模糊:共享所有权让代码难以推理对象的生命周期,谁负责释放?何时释放?这违背了现代 C++ 强调的“明确所有权”(clear ownership)。
- 设计问题信号:如果到处用
shared_ptr,往往表示设计不良——本该用值语义、唯一所有权或观察者模式,却懒惰地用共享来“延长生命周期”。
C++ Core Guidelines(由 Bjarne Stroustrup 等维护的官方指南)明确建议:
- 优先用
unique_ptr表示独占所有权(R.21)。 - 只在真正需要共享所有权时用
shared_ptr(作为最后手段)。 - 函数参数:如果不转移所有权,用普通指针(
T*)或引用(T&);如果可能保留拷贝,用const shared_ptr<T>&。 - Sean Parent(Adobe C++ 专家)曾说:“
shared_ptr就像全局变量一样糟糕。”
Reddit 和 Stack Overflow 上常见讨论(如 2024-2025 的帖子)也指出:许多代码像“Java 风格的 C++”,到处堆 shared_ptr,导致“Java-in-C++”式的滥用。甚至有观点认为,如果你的代码需要大量 shared_ptr,很可能设计有问题——应该重构为层次结构、值语义或 unique_ptr + 观察者。
什么时候应该用 shared_ptr?
只有在真正共享所有权的场景:
- 对象生命周期不由单一所有者控制(如多线程共享资源、图结构中的节点)。
- 需要弱引用打破循环(配合
weak_ptr)。 - 工厂函数返回共享对象。
否则:
- 用值语义(直接对象)。
- 用
unique_ptr(独占所有权,可移动)。 - 用普通引用/指针(非拥有,仅观察)。
总结与建议
过度使用 shared_ptr 已成现代 C++ 中的常见反模式,尤其在新手从 Java/Python 转来时。社区共识是:它不是默认选择,而是例外。优先明确所有权、减少动态分配,能显著提升代码可读性、性能和安全性。
如果你在项目中看到到处是 shared_ptr,建议审视设计——很可能有更好的方式。参考 C++ Core Guidelines 的智能指针部分,会很有帮助。