ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

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

C++智能指针完全指南:unique_ptr、shared_ptr、weak_ptr 适用场景深度解析

一、为什么需要智能指针?

传统C++中,手动管理动态内存容易导致:

  • 内存泄漏:忘记delete

  • 悬空指针:释放后继续使用

  • 二次释放:多个指针指向同一块内存

智能指针通过RAII机制自动管理资源生命周期,从C++11起成为现代C++标配。


二、unique_ptr——独占所有权

核心特性

  • 独占所有权,不可拷贝

  • 轻量级,性能接近裸指针

  • 可移动转移所有权

  • 默认使用delete释放,支持自定义删除器

适用场景

✅ 工厂函数返回对象
std::unique_ptr<Widget> createWidget() { return std::make_unique<Widget>(); }
✅ 类成员变量(独占资源)
class DatabaseConnection { std::unique_ptr<Connection> conn_; public: DatabaseConnection() : conn_(std::make_unique<Connection>()) {} };
✅ 容器存储多态对象
std::vector<std::unique_ptr<Shape>> shapes; shapes.push_back(std::make_unique<Circle>()); shapes.push_back(std::make_unique<Rectangle>());
✅ PImpl惯用法
// Widget.h class Widget { class Impl; std::unique_ptr<Impl> pImpl; }; // Widget.cpp class Widget::Impl { /* ... */ };

❌ 不适合场景

  • 需要共享所有权时

  • 需要在多个地方同时引用同一个对象


三、shared_ptr——共享所有权

核心特性

  • 引用计数共享所有权

  • 最后一个shared_ptr销毁时释放资源

  • 线程安全(引用计数操作原子化)

  • 比unique_ptr重(额外维护控制块)

适用场景

✅ 多个对象共享同一资源
class Texture { // 大型纹理数据 }; std::shared_ptr<Texture> tex1 = std::make_shared<Texture>(); auto tex2 = tex1; // 两个对象共享同一纹理
✅ 回调与异步操作
void asyncProcess(std::shared_ptr<Data> data) { std::thread([data]() { // 确保异步执行期间data存活 process(data); }).detach(); }
✅ 缓存系统
class CacheManager { std::map<std::string, std::shared_ptr<ExpensiveObject>> cache; std::shared_ptr<ExpensiveObject> get(const std::string& key) { auto it = cache.find(key); if (it != cache.end()) return it->second; auto obj = std::make_shared<ExpensiveObject>(); cache[key] = obj; return obj; } };
✅ 复杂数据结构中的共享节点
struct TreeNode { int value; std::shared_ptr<TreeNode> left, right; std::weak_ptr<TreeNode> parent; // 避免循环引用 };

❌ 不适合场景

  • 简单独占所有权(用unique_ptr更高效)

  • 存在循环引用可能时(需配合weak_ptr)

  • 对性能极度敏感的场景


四、weak_ptr——弱引用观察者

核心特性

  • 不增加引用计数

  • 不能直接访问对象,必须lock()提升为shared_ptr

  • 解决shared_ptr循环引用问题

  • 用于观察资源是否存活

适用场景

✅ 打破循环引用
class Child { std::shared_ptr<Parent> parent_; // 错误!循环引用 std::weak_ptr<Parent> parent_; // 正确 }; class Parent { std::vector<std::shared_ptr<Child>> children_; };
✅ 缓存失效检测
class ExpensiveCache { std::map<int, std::weak_ptr<ExpensiveObj>> cache; std::shared_ptr<ExpensiveObj> 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_shared<ExpensiveObj>(id); cache[id] = obj; return obj; } };
✅ 观察者模式
class Observer { std::weak_ptr<Subject> subject_; public: void update() { if (auto sub = subject_.lock()) { // 安全地访问subject } } };
✅ 临时访问非拥有资源
class ResourcePool { std::vector<std::weak_ptr<Resource>> resources_; std::shared_ptr<Resource> acquire() { for (auto& wp : resources_) { if (auto r = wp.lock()) { return r; // 复用已有资源 } } return nullptr; } };

五、实战对比表

特性

unique_ptr

shared_ptr

weak_ptr

所有权

独占

共享

无(观察)

引用计数

有(原子操作)

拷贝语义

禁止

允许(计数+1)

允许

移动语义

允许

允许

允许

性能开销

极小

较大

中等

内存占用

同裸指针

约多8字节

约多4字节

线程安全

引用计数安全

同shared_ptr

自定义删除器

支持

支持

不支持


六、最佳实践总结

选择原则

  1. 优先unique_ptr:除非明确需要共享所有权

  2. 使用make_shared/make_unique:异常安全且效率高

  3. 警惕循环引用:用weak_ptr打断

  4. 避免裸指针传递所有权:用unique_ptr::get()仅作临时访问

常见反模式

// ❌ 不要这样做 auto sp = std::make_shared<int>(42); std::weak_ptr<int> 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_this<Node> { public: std::shared_ptr<Node> getShared() { return shared_from_this(); } };

别名构造函数(C++17)

auto ptr = std::make_shared<Foo>(args); std::shared_ptr<Bar> alias(ptr, &ptr->bar); // 共享控制块,但指向成员

数组支持(C++17)

auto arr = std::make_unique<int[]>(100); auto arr_sp = std::shared_ptr<int[]>(new int[100], std::default_delete<int[]>());

掌握这三种智能指针的适用场景,能让你写出更安全、更高效的C++代码。记住:正确的所有权设计比任何优化都重要

返回列表