马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
各人好呀,我是灰灰,上期咱们聊完引用,不少小搭档在品评区哭诉内存走漏的凄惨履历。本日咱们就来解锁C++的"自动驾驶"神器——智能指针!今后告别new/delete的手动挡期间,系好安全带,发车啦!🚗💨
一、智能指针是什么?为什么须要它?
1.1 手动管理内存的痛
- void 作死示例() {
- int* 裸指针 = new int[10086]; // 申请
- // ... 一顿操作猛如虎 ...
- if(rand() % 2) return; // 50%概率忘记释放!
- delete[] 裸指针; // 内存泄漏的万恶之源
- }
复制代码 经典翻车现场:
- 忘记delete → 内存走漏
- 提前delete → 野指针
- 非常导致跳过delete → 走漏
1.2 智能指针的救世主本质
智能指针就像你的私人管家,它本质上是一个类,当失去作用时会调用其析构函数,自动开释内存:
1️⃣ 自动开释:对象烧毁时自动整理内存
2️⃣ 非常安全:哪怕步调瓦解也不走漏
3️⃣ 全部权明确:清晰表达资源归属
二、智能指针创建三大姿势
智能指针的根本模板是:- std::unique_ptr<T, DeleterType> ptr(原始指针, 删除器);
复制代码 因此我们只须要将须要管理的指针交给它托管。
(删除器是可选项,默认是利用delete或delete[],但是遇到一些不能通过delete开释的资源,好比管理fopen()返回的FILE*(需用fclose()关闭),就须要自界说删除器了,如果有须要以后会有专门一篇文章解说哦)
1.1 直接构造法(传统技能)
- // 裸指针转智能指针(慎用!)
- std::unique_ptr<int> 管家1(new int(42));
- std::shared_ptr<std::string> 管家2(new std::string("Hello"));
复制代码 实用场景:
伤害告诫:- int* 裸指针 = new int(10);
- std::shared_ptr<int> 管家A(裸指针);
- std::shared_ptr<int> 管家B(裸指针);
- // 双重释放!两个管家各自为战
复制代码 1.2 工厂函数法(官方保举)
- auto 安全管家1 = std::make_unique<int>(42); // C++14+
- auto 安全管家2 = std::make_shared<std::string>("Hello");
复制代码 上风对比:
创建方式内存分配次数非常安全代码简便度new + 构造函数2次❌❌make_shared1次✅✅代码示例:- // 创建数组的专属姿势
- auto 数组管家 = std::make_unique<int[]>(5); // 5个int的数组
- 数组管家[0] = 1; // 像普通数组一样使用
- // 自定义类型
- class 学生 {
- public:
- 学生(string 名, int 分) : 名字(名), 分数(分) {}
- };
- auto 学霸 = std::make_shared<学生>("张三", 100);
复制代码 1.3 空指针初始化法(耽误加载)
- std::shared_ptr<文件类> 文件管家 = nullptr;
- if(需要加载文件) {
- 文件管家 = std::make_shared<文件类>("data.bin");
- }
复制代码 当创建好智能指针后,你完全可以把它看成一个平凡的指针去利用它,但是牢记不能delete哦(这是管家的工作)
三、三大智能指针详解
2.1 unique_ptr:埋头霸道总裁
特点:
- 独占资源,拒绝共享(move-only)
- 零额外开销(和裸指针一样高效)
利用场景:
- 明确资源唯一全部者的场景
- 替换auto_ptr(C++17已废弃)
代码实战:- // 创建独占文件句柄
- std::unique_ptr<FILE, decltype(&fclose)> 文件管家(fopen("data.txt", "r"), fclose);
- // 自动管理动态数组
- auto 数组管家 = std::make_unique<int[]>(100);
- // 所有权转移(交出控制权)
- auto 新管家 = std::move(数组管家);
- // 此时数组管家变为nullptr
复制代码 避坑指南:
- ❌ 克制拷贝构造(会编译报错)
- ✅ 用reset()自动开释资源
- ✅ 通过get()获取裸指针(但不要手动delete!)
2.2 shared_ptr:共享经济达人
特点:
- 通过引用计数共享资源
- 计数归零自动开释
- 支持自界说删除器
代码实战:- class 大对象 {
- public:
- ~大对象() { cout << "我挂了..."; }
- };
- // 创建共享资源
- auto 共享资源 = std::make_shared<大对象>();
- {
- auto 副本1 = 共享资源; // 引用计数+1
- auto 副本2 = 共享资源; // 引用计数+1 → 3
- } // 离开作用域计数-2 → 1
- } // 最后离开作用域计数归零,触发析构
复制代码 经典翻车现场:循环引用- class 甲 {
- public:
- std::shared_ptr<乙> 乙指针;
- };
- class 乙 {
- public:
- std::shared_ptr<甲> 甲指针;
- };
- auto 甲哥 = std::make_shared<甲>();
- auto 乙哥 = std::make_shared<乙>();
- 甲哥->乙指针 = 乙哥;
- 乙哥->甲指针 = 甲哥; // 引用计数永远≥1,内存泄漏!
复制代码 2.3 weak_ptr:突破循环的钥匙
特点:
- 观察者模式,不增长引用计数
- 需通过lock()获取可用的shared_ptr
救济循环引用:- class 乙 {
- public:
- std::weak_ptr<甲> 甲指针; // 改用weak_ptr
- };
- auto 甲哥 = std::make_shared<甲>();
- auto 乙哥 = std::make_shared<乙>();
- 甲哥->乙指针 = 乙哥;
- 乙哥->甲指针 = 甲哥; // 现在可以正确释放了!
- // 使用示例
- if(auto 临时管家 = 乙哥->甲指针.lock()) {
- 临时管家->操作(); // 安全访问
- } else {
- cout << "对象已释放";
- }
复制代码 四、智能指针性能揭秘
3.1 性能对比表
指针范例内存开销线程安全实用场景unique_ptr0额外开销非线程安全独占资源、性能敏感shared_ptr2倍指针巨细引用计数原子利用共享资源weak_ptr2倍指针巨细同shared_ptr突破循环引用3.2 最佳实践
1️⃣ 优先利用make_shared/make_unique:
- 克制内存碎片
- 非常安全(不会出现new完没赋给指针的环境)
❌ 伤害利用:- process(shared_ptr<大对象>(new 大对象), 其他函数());
- // 若`其他函数()`抛出异常,可能泄漏!
复制代码 ✅ 安全写法:- process(std::make_shared<大对象>(), 其他函数());
复制代码 五、智能指针常用函数手册
3.1 通用函数百口桶
函数作用示例reset()开释当前资源管家.reset();get()获取裸指针(慎用!)int* p = 管家.get();operator->访问成员管家->存款 += 100;operator*解引用int value = *管家;operator bool判断是否为空if(管家) { ... } 3.2 shared_ptr专属技能
函数作用示例use_count()查察引用计数cout << 管家.use_count();unique()判断是否唯一全部者if(管家.unique()) { ... }swap()交换两个shared_ptr管家A.swap(管家B); 3.3 unique_ptr黑科技
函数作用示例release()开释全部权返回裸指针int* p = 管家.release();swap()交换两个unique_ptr管家A.swap(管家B); 六、避坑宝典(血泪履历)
❌ 大坑1:混用裸指针和智能指针
- auto 管家 = std::make_shared<大对象>();
- 大对象* 裸指针 = 管家.get();
- delete 裸指针; // 导致双重释放!
- 管家->操作(); // 程序崩溃!
复制代码 ❌ 大坑2:在多个shared_ptr中托管同一裸指针
- auto* 裸指针 = new 大对象;
- std::shared_ptr<大对象> 管家1(裸指针);
- std::shared_ptr<大对象> 管家2(裸指针);
- // 双重释放!引用计数不共享
复制代码 ✅ 精确姿势:始终利用make_shared
- auto 安全管家1 = std::make_shared<大对象>();
- auto 安全管家2 = 安全管家1; // 共享计数
复制代码 七、总结:智能指针三定律
1️⃣ 能用智能指针就不消裸指针:
2️⃣ 能unique就不shared:
- 优先用unique_ptr,必须共享再用shared_ptr
3️⃣ 循环引用必用weak_ptr:
智能指针就像C++天下的自动驾驶体系——你负责指明方向(业务逻辑),它负责安全驾驶(内存管理)。下期想听什么?品评区告诉!咱们《C++移动语义:从深拷贝到零本钱搬运》见! 🚀
|