当前位置: 首页 > news >正文

【C++】零基础入门 · 第 14 节:智能指针(unique_ptr、shared_ptr、weak_ptr)

在第 9 节中,我们学习了newdelete来手动管理动态内存,在第 13 节中我们了解了异常处理和 RAII 原则。今天,我们来学习一个把这两者完美结合的工具——智能指针(Smart Pointer)

智能指针的核心理念很简单:让对象自动管理内存,程序员不需要手动调用delete它是现代 C++ 中最重要的特性之一,也是写出安全、不泄漏内存的代码的关键。

1. 为什么需要智能指针

1.1 裸指针的烦恼

回顾一下使用裸指针(raw pointer)的问题:

voidrisky(){int*ptr=newint(42);// 如果这里发生了异常...doSomething();// 可能抛出异常deleteptr;// 这行可能永远执行不到!}

如果doSomething()抛出异常,delete ptr不会被执行,内存就泄漏了。虽然我们可以用try-catch来解决,但代码会变得非常臃肿。

1.2 智能指针的解决方案

智能指针利用 RAII 原则:在构造时获取资源(new),在析构时释放资源(delete)。当智能指针离开作用域时,析构函数会自动被调用,即使是因为异常导致的离开。

#include<memory>voidsafe(){std::unique_ptr<int>ptr=std::make_unique<int>(42);doSomething();// 即使抛出异常,ptr 的析构函数也会自动调用// 不需要手动 delete!}

2. unique_ptr:独占所有权

2.1 基本用法

unique_ptr是最常用的智能指针。它表示独占所有权——同一时刻只有一个unique_ptr拥有某个对象。

#include<iostream>#include<memory>usingnamespacestd;intmain(){// 创建 unique_ptr(推荐使用 make_unique)unique_ptr<int>ptr=make_unique<int>(42);// 通过 * 解引用访问值cout<<"值:"<<*ptr<<endl;// 输出:42// 通过 -> 访问成员(用于对象时)// ptr->member// 离开作用域时自动释放内存return0;}

2.2 不可复制,只能移动

unique_ptr不允许复制(copy),因为复制会导致两个指针指向同一块内存,违反了「独占」的原则。但它允许移动(move):

#include<iostream>#include<memory>usingnamespacestd;intmain(){unique_ptr<int>ptr1=make_unique<int>(42);// unique_ptr<int> ptr2 = ptr1; // 编译错误!不能复制unique_ptr<int>ptr2=std::move(ptr1);// 移动所有权// 移动后,ptr1 变为空指针cout<<"ptr1:"<<(ptr1?"有效":"为空")<<endl;// 输出:为空cout<<"ptr2:"<<*ptr2<<endl;// 输出:42return0;}

2.3 管理数组

unique_ptr也可以管理动态数组:

#include<iostream>#include<memory>usingnamespacestd;intmain(){unique_ptr<int[]>arr=make_unique<int[]>(5);for(inti=0;i<5;i++){arr[i]=i*10;}for(inti=0;i<5;i++){cout<<arr[i]<<" ";// 输出:0 10 20 30 40}cout<<endl;// 自动调用 delete[] 释放return0;}

2.4 自定义删除器

有时候你需要在释放资源时执行自定义操作(比如关闭文件、释放网络连接):

#include<iostream>#include<memory>#include<cstdio>usingnamespacestd;intmain(){// 用 unique_ptr 管理 FILE*,自定义删除器调用 fcloseunique_ptr<FILE,decltype(&fclose)>file(fopen("test.txt","w"),&fclose);if(file){fprintf(file.get(),"Hello, Smart Pointer!\n");}// file 离开作用域时自动调用 fclosereturn0;}

3. shared_ptr:共享所有权

3.1 基本用法

shared_ptr表示共享所有权——多个shared_ptr可以同时拥有同一个对象。它内部维护一个引用计数,当最后一个shared_ptr被销毁时,才会释放内存。

#include<iostream>#include<memory>usingnamespacestd;intmain(){shared_ptr<int>ptr1=make_shared<int>(42);cout<<"引用计数:"<<ptr1.use_count()<<endl;// 输出:1{shared_ptr<int>ptr2=ptr1;// 复制,引用计数 +1cout<<"引用计数:"<<ptr1.use_count()<<endl;// 输出:2}// ptr2 离开作用域,引用计数 -1cout<<"引用计数:"<<ptr1.use_count()<<endl;// 输出:1return0;}// ptr1 离开作用域,引用计数归零,自动释放内存

3.2 shared_ptr 的开销

shared_ptrunique_ptr有更多的内存和性能开销:

  • 每个shared_ptr需要额外存储一个指向控制块的指针
  • 控制块中包含引用计数、弱引用计数、删除器等信息
  • 引用计数的增减是原子操作,有微小的性能开销

所以:能用unique_ptr就不要用shared_ptr

3.3 make_shared vs new

推荐使用make_shared而不是new来创建shared_ptr

// 推荐:一次内存分配(对象和控制块一起分配)autoptr=make_shared<int>(42);// 不推荐:两次内存分配(一次 new 对象,一次分配控制块)shared_ptr<int>ptr(newint(42));

make_shared不仅代码更简洁,而且性能更好(减少一次内存分配)。

4. weak_ptr:打破循环引用

4.1 循环引用的问题

shared_ptr有一个致命的陷阱——循环引用

#include<iostream>#include<memory>usingnamespacestd;structB;// 前向声明structA{shared_ptr<B>b_ptr;~A(){cout<<"A 被销毁"<<endl;}};structB{shared_ptr<A>a_ptr;~B(){cout<<"B 被销毁"<<endl;}};intmain(){autoa=make_shared<A>();autob=make_shared<B>();a->b_ptr=b;// A 指向 Bb->a_ptr=a;// B 指向 A// 离开作用域后,a 的引用计数 = 1(被 b->a_ptr 引用)// b 的引用计数 = 1(被 a->b_ptr 引用)// 两者都不会归零,内存泄漏!return0;}

运行这段代码,你会发现A 和 B 的析构函数都不会被调用——内存泄漏了。

4.2 weak_ptr 的解决方案

weak_ptrshared_ptr的「观察者」,它不增加引用计数。通过weak_ptr可以打破循环引用:

#include<iostream>#include<memory>usingnamespacestd;structB;structA{shared_ptr<B>b_ptr;~A(){cout<<"A 被销毁"<<endl;}};structB{weak_ptr<A>a_ptr;// 改用 weak_ptr!~B(){cout<<"B 被销毁"<<endl;}};intmain(){autoa=make_shared<A>();autob=make_shared<B>();a->b_ptr=b;b->a_ptr=a;return0;}// 输出:// A 被销毁// B 被销毁

4.3 使用 weak_ptr 访问对象

weak_ptr不能直接访问对象,需要先调用lock()获取一个shared_ptr

weak_ptr<int>wp=...;// 使用前先检查对象是否还存在if(autosp=wp.lock()){cout<<"对象存在,值为:"<<*sp<<endl;}else{cout<<"对象已被销毁"<<endl;}

lock()的行为:

  • 如果对象还存在(引用计数 > 0),返回一个有效的shared_ptr
  • 如果对象已被销毁,返回一个空的shared_ptr

5. 三种智能指针对比

特性unique_ptrshared_ptrweak_ptr
所有权独占共享不拥有(观察)
可复制
可移动-
引用计数不增加计数
内存开销最小较大(控制块)较小
典型场景独占资源管理共享资源打破循环引用
创建方式make_uniquemake_sharedshared_ptr创建

6. 使用建议

6.1 优先使用 unique_ptr

如果你的场景中只有一个指针拥有对象,用unique_ptr。它没有额外开销,语义也最清晰。

6.2 需要共享时用 shared_ptr

当多个地方需要共享同一个对象,且对象的生命周期不确定时,用shared_ptr

6.3 用 weak_ptr 打破循环

如果两个shared_ptr互相引用,把其中一个改为weak_ptr

6.4 函数参数传递

// 只读访问:传引用或裸指针voidprocess(constWidget&widget);voidprocess(Widget*widget);// 接收所有权:传 unique_ptr(按值)voidtakeOwnership(unique_ptr<Widget>widget);// 共享所有权:传 shared_ptr(按值或按引用)voidshare(shared_ptr<Widget>widget);

6.5 不要混用裸指针和智能指针

// 错误!两个独立的智能指针管理同一块内存int*raw=newint(42);unique_ptr<int>p1(raw);unique_ptr<int>p2(raw);// p1 和 p2 都会 delete raw,双重释放!// 正确:只通过智能指针创建autop1=make_unique<int>(42);

7. 总结

这一节我们学习了 C++ 的三种智能指针:

  • unique_ptr:独占所有权,不可复制,推荐首选。
  • shared_ptr:共享所有权,引用计数管理,有额外开销。
  • weak_ptr:观察者,不增加引用计数,用于打破循环引用。

智能指针是现代 C++ 内存管理的基石。掌握了它们,你就再也不需要担心内存泄漏和悬垂指针的问题。下一节我们将继续探索 C++ 的更多高级特性。加油!

http://www.zskr.cn/news/1436344.html

相关文章:

  • 应用安全 --- IDAPro脚本 之 导出函数引用数据
  • 开源 AI Agent Harness Engineering 框架横向对比评测
  • 2026年GEO系统源码公司权威评测:源头厂商与贴牌避坑指南 - 品牌报告
  • 密钥轮换失效、设备绑定丢失、会话劫持频发——Gemini企业级身份验证故障全解析,一线SRE连夜修复的3个致命配置
  • 郑州市 惠济区 上门安装、维修维保|维小达 开关插座/灯具/门窗/柜体/锁具/卫浴/龙头/洗菜盆/踢脚线一站式家装安装服务 - 维小达科技
  • 论文反复修改到心累?资深导师力荐这几个AI论文平台
  • 照着用就行:2026年实打实好用的专业降AIGC软件
  • 芜湖黄金店哪家价格最划算? - 鸿运名品
  • 02 基础语法 JavaScript 入门到精通全套教程 19-33
  • Visuino图形化编程实现Arduino舵机交互控制:从按钮到PWM的实践指南
  • Python协程实战:异步高效爬取《鬼神传》全本小说
  • 基于Arduino与433MHz模块的无线距离报警器设计与实现
  • 3步掌握YimMenu:GTA5开源防护工具完全实战指南
  • Jamstack开发:构建高性能静态网站
  • 黄大年茶思屋榜文132期 储能篇 第1题 储能锂离子大电芯析锂无损检测
  • Ubuntu 22.04上vsftpd的550目录切换错误,别急着改权限,先看看这个chroot配置
  • 深度学习生成模型(三)—— 扩散模型:DDPM 与 Stable Diffusion(五十一)
  • 基于Arduino的随机按键门锁:用动态映射提升物理安全
  • Latest Verification Report of Official Rolex After-Sales Service Centers – June 2026 - 资讯纵览
  • 别再被查重费割韭菜了!这个AI平台的免费查重功能,99%的毕业生还不知道
  • 深度学习生成模型(四)—— 自编码器与表征学习(五十二)
  • 基于Arduino的AI猜数游戏:从有限状态机到模块化智能体设计
  • 百度网盘秒传脚本:5分钟快速上手,告别文件分享失效烦恼
  • 手把手教你离线搞定CUDA和cuDNN:从下载到配置,再到打包迁移完整流程(含超算实战)
  • Gemini跨境数据脱敏策略失效真相:动态掩码密钥轮转机制(附AWS KMS+HashiCorp Vault双活配置模板)
  • 基于TCS3200与Arduino的智能画框灯光反馈系统实战
  • Gemini服务条款变更实录:从免费试用到商用收费的3个临界点,及替代方案迁移时间窗(仅剩18天)
  • 构建高可用音乐播放器:洛雪音乐多平台音源集成实战指南
  • 2026年10款论文降AI率网站横评:从90%降至10%的宝藏之选
  • 解锁2026浪琴官方售后新体验:实地鉴证服务全面革新新址及售后热线启用 - 资讯纵览