资讯动态

C++智能指针完全指南:unique_ptr、shared_ptr、weak_ptr

发布时间:2026/8/5 2:25:21 来源:尧图企业网站定制
C智能指针完全指南unique_ptr、shared_ptr、weak_ptr 适用场景深度解析一、为什么需要智能指针传统C中手动管理动态内存容易导致内存泄漏忘记delete悬空指针释放后继续使用二次释放多个指针指向同一块内存智能指针通过RAII机制自动管理资源生命周期从C11起成为现代C标配。二、unique_ptr——独占所有权核心特性独占所有权不可拷贝轻量级性能接近裸指针可移动转移所有权默认使用delete释放支持自定义删除器适用场景✅ 工厂函数返回对象std::unique_ptrWidget createWidget() { return std::make_uniqueWidget(); }✅ 类成员变量独占资源class DatabaseConnection { std::unique_ptrConnection conn_; public: DatabaseConnection() : conn_(std::make_uniqueConnection()) {} };✅ 容器存储多态对象std::vectorstd::unique_ptrShape shapes; shapes.push_back(std::make_uniqueCircle()); shapes.push_back(std::make_uniqueRectangle());✅ PImpl惯用法// Widget.h class Widget { class Impl; std::unique_ptrImpl pImpl; }; // Widget.cpp class Widget::Impl { /* ... */ };❌ 不适合场景需要共享所有权时需要在多个地方同时引用同一个对象三、shared_ptr——共享所有权核心特性引用计数共享所有权最后一个shared_ptr销毁时释放资源线程安全引用计数操作原子化比unique_ptr重额外维护控制块适用场景✅ 多个对象共享同一资源class Texture { // 大型纹理数据 }; std::shared_ptrTexture tex1 std::make_sharedTexture(); auto tex2 tex1; // 两个对象共享同一纹理✅ 回调与异步操作void asyncProcess(std::shared_ptrData data) { std::thread([data]() { // 确保异步执行期间data存活 process(data); }).detach(); }✅ 缓存系统class CacheManager { std::mapstd::string, std::shared_ptrExpensiveObject cache; std::shared_ptrExpensiveObject get(const std::string key) { auto it cache.find(key); if (it ! cache.end()) return it-second; auto obj std::make_sharedExpensiveObject(); cache[key] obj; return obj; } };✅ 复杂数据结构中的共享节点struct TreeNode { int value; std::shared_ptrTreeNode left, right; std::weak_ptrTreeNode parent; // 避免循环引用 };❌ 不适合场景简单独占所有权用unique_ptr更高效存在循环引用可能时需配合weak_ptr对性能极度敏感的场景四、weak_ptr——弱引用观察者核心特性不增加引用计数不能直接访问对象必须lock()提升为shared_ptr解决shared_ptr循环引用问题用于观察资源是否存活适用场景✅ 打破循环引用class Child { std::shared_ptrParent parent_; // 错误循环引用 std::weak_ptrParent parent_; // 正确 }; class Parent { std::vectorstd::shared_ptrChild children_; };✅ 缓存失效检测class ExpensiveCache { std::mapint, std::weak_ptrExpensiveObj cache; std::shared_ptrExpensiveObj get(int id) { auto it cache.find(id); if (it ! cache.end()) { if (auto obj it-second.lock()) return obj; // 缓存命中且有效 else cache.erase(it); // 清理过期条目 } auto obj std::make_sharedExpensiveObj(id); cache[id] obj; return obj; } };✅ 观察者模式class Observer { std::weak_ptrSubject subject_; public: void update() { if (auto sub subject_.lock()) { // 安全地访问subject } } };✅ 临时访问非拥有资源class ResourcePool { std::vectorstd::weak_ptrResource resources_; std::shared_ptrResource acquire() { for (auto wp : resources_) { if (auto r wp.lock()) { return r; // 复用已有资源 } } return nullptr; } };五、实战对比表特性unique_ptrshared_ptrweak_ptr所有权独占共享无观察引用计数无有原子操作无拷贝语义禁止允许计数1允许移动语义允许允许允许性能开销极小较大中等内存占用同裸指针约多8字节约多4字节线程安全是引用计数安全同shared_ptr自定义删除器支持支持不支持六、最佳实践总结选择原则优先unique_ptr除非明确需要共享所有权使用make_shared/make_unique异常安全且效率高警惕循环引用用weak_ptr打断避免裸指针传递所有权用unique_ptr::get()仅作临时访问常见反模式// ❌ 不要这样做 auto sp std::make_sharedint(42); std::weak_ptrint wp sp; if (!wp.expired()) { // 竞态条件 auto sp2 wp.lock(); // 可能已经失效 } // ✅ 正确做法 if (auto sp2 wp.lock()) { // 安全使用sp2 }现代C资源管理准则不使用new/delete除非实现底层库所有动态资源用智能指针包装接口参数用智能指针明确所有权语义返回对象用unique_ptr接受共享用shared_ptr七、进阶技巧enable_shared_from_this当需要在类内部获取自身的shared_ptr时class Node : public std::enable_shared_from_thisNode { public: std::shared_ptrNode getShared() { return shared_from_this(); } };别名构造函数C17auto ptr std::make_sharedFoo(args); std::shared_ptrBar alias(ptr, ptr-bar); // 共享控制块但指向成员数组支持C17auto arr std::make_uniqueint[](100); auto arr_sp std::shared_ptrint[](new int[100], std::default_deleteint[]());掌握这三种智能指针的适用场景能让你写出更安全、更高效的C代码。记住正确的所有权设计比任何优化都重要。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价