新闻详情

新闻详情

首页 / 资讯中心 / 详情

C++ 智能指针完全指南(一):unique_ptr 深度详解

发布时间:2026/8/31 15:13:44来源:尧图网络
C++ 智能指针完全指南(一):unique_ptr 深度详解
引言C 程序员最头疼的问题是什么不是语法、不是模板、不是多线程——而是内存管理。void func() { int* p new int(42); // ... 复杂逻辑 ... if (某个条件) return; // 忘记 delete p → 内存泄漏 // ... 更多逻辑 ... delete p; // 如果中间抛异常永远执行不到这里 }手动new/delete有三大痛点忘记 delete→ 内存泄漏提前 return 或抛异常→ delete 被跳过多次 delete→ 程序崩溃智能指针就是来彻底解决这些问题的。从 C11 开始标准库提供了三种智能指针unique_ptr、shared_ptr、weak_ptr。它们本质上是用 RAII 包装原始指针让指针在离开作用域时自动释放。本文作为智能指针系列第一篇聚焦于独占所有权的unique_ptr。第一部分为什么需要 unique_ptr一、原始指针的困境#include iostream using namespace std; class Resource { public: Resource() { cout 资源获取 endl; } ~Resource() { cout 资源释放 endl; } void work() { cout 工作中... endl; } }; void badExample() { Resource* p new Resource(); // 场景1提前 return忘记 delete if (rand() % 2 0) { return; // 内存泄漏p 没有被释放 } // 场景2抛异常delete 被跳过 p-work(); throw runtime_error(出错了); // 异常抛出 → delete 不执行 delete p; // 只有正常情况才能执行到这里 }三个场景只有一种情况能正确释放内存。这就是为什么大型 C 项目普遍禁止裸new/delete。二、unique_ptr 的解决方案#include memory void goodExample() { unique_ptrResource p make_uniqueResource(); // 场景1提前 return → p 的析构函数自动调用 → 资源释放 ✅ if (rand() % 2 0) return; // 场景2抛异常 → p 的析构函数仍然会被调用 → 资源释放 ✅ p-work(); throw runtime_error(出错了); // 不需要 delete离开作用域自动释放 ✅ }第二部分unique_ptr 的核心特性一、独占所有权unique_ptr独占它指向的对象不能被拷贝只能被移动。unique_ptrint p1 make_uniqueint(42); unique_ptrint p2 p1; // ❌ 编译错误不能拷贝 unique_ptrint p3 std::move(p1); // ✅ 移动后 p1 变成 nullptr二、三种创建方式#include memory using namespace std; int main() { // 方式1make_uniqueC14最推荐 auto p1 make_uniqueint(42); // 方式2用 newC11 兼容 unique_ptrint p2(new int(42)); // 方式3从已有 unique_ptr 移动 unique_ptrint p3 std::move(p1); // p1 变成 nullptr return 0; // p2 和 p3 自动释放p1 已经为空 }优先用make_unique原因代码更简洁不需要写两次类型名异常安全C14 起可用三、访问被管理的对象unique_ptrstring p make_uniquestring(hello); // 像指针一样使用 cout *p endl; // hello解引用 cout p-size() endl; // 5访问成员 cout p.get() endl; // 获取原始指针谨慎使用操作含义*p解引用获取对象的引用p-访问对象成员p.get()获取原始指针不转移所有权p nullptr判断是否为空if(p)判断是否为空四、释放与重置unique_ptrint p make_uniqueint(42); p.reset(); // 释放资源p 变成 nullptr p.reset(new int(100)); // 释放旧资源接管新资源 int* raw p.release(); // 放弃所有权返回原始指针 // p 变成 nullptrraw 需要手动 delete delete raw;操作含义p.reset()释放资源p 为空p.reset(newPtr)释放旧资源接管新资源p.release()放弃所有权返回裸指针需要手动 delete第三部分unique_ptr 的常用场景一、作为函数返回值最安全unique_ptrResource createResource() { return make_uniqueResource(); // 返回临时对象 → 自动移动 } int main() { auto p createResource(); // 接收移动 p-work(); }二、放入容器vectorunique_ptrResource resources; resources.push_back(make_uniqueResource()); // 移动进去 resources.emplace_back(make_uniqueResource()); // 遍历 for (const auto r : resources) { r-work(); } // 容器销毁时所有 unique_ptr 自动释放 → 不用手动删三、工厂模式class Product { public: virtual void use() 0; virtual ~Product() default; }; class ProductA : public Product { public: void use() override { cout 使用产品A endl; } }; class ProductB : public Product { public: void use() override { cout 使用产品B endl; } }; // 工厂函数返回 unique_ptr基类 unique_ptrProduct createProduct(char type) { if (type A) return make_uniqueProductA(); if (type B) return make_uniqueProductB(); return nullptr; } int main() { auto p createProduct(A); p-use(); // 使用产品A // 多态析构需要基类有虚析构函数 }第四部分自定义删除器一、什么时候需要自定义删除器默认的删除器是delete但有些资源不是用new分配的// 文件句柄 FILE* f fopen(test.txt, r); // 用完后需要 fclose(f)不是 delete f // Socket int sockfd socket(...); // 用完后需要 close(sockfd)不是 delete sockfd // 自定义内存池 void* mem my_pool_alloc(1024); // 用完后需要 my_pool_free(mem)不是 delete mem二、自定义删除器示例#include cstdio // 文件句柄管理 auto fileDeleter [](FILE* f) { if (f) { cout 关闭文件 endl; fclose(f); } }; int main() { FILE* f fopen(test.txt, w); unique_ptrFILE, decltype(fileDeleter) filePtr(f, fileDeleter); fprintf(filePtr.get(), hello\n); // 离开作用域 → 自动调用 fclose }// Socket 管理 auto socketDeleter [](int* fd) { if (fd *fd ! -1) { cout 关闭 socket endl; close(*fd); } delete fd; }; int main() { int* sockfd new int(socket(AF_INET, SOCK_STREAM, 0)); unique_ptrint, decltype(socketDeleter) sockPtr(sockfd, socketDeleter); // 离开作用域 → 自动关闭 socket }三、unique_ptr 管理数组// 管理动态数组C14 起 auto arr make_uniqueint[](10); // 10 个 int arr[0] 1; arr[1] 2; cout arr[3] endl; // 离开作用域 → 自动调用 delete[] // 注意shared_ptr 也支持数组C17 起第五部分unique_ptr vs 原始指针对比项原始指针unique_ptr内存释放手动 delete自动所有权不明确独占明确拷贝浅拷贝可能出问题禁止拷贝异常安全❌ 可能泄漏✅ 保证释放性能开销无零开销和原始指针一样大小8 字节8 字节无自定义删除器时unique_ptr 是零开销抽象在编译优化后unique_ptr的大小和性能与原始指针完全一样。第六部分常见错误// ❌ 错误1试图拷贝 unique_ptr unique_ptrint p1 make_uniqueint(42); unique_ptrint p2 p1; // 编译错误 // ❌ 错误2把同一个裸指针给多个 unique_ptr int* raw new int(42); unique_ptrint p3(raw); unique_ptrint p4(raw); // p3 和 p4 都会尝试 delete raw → 双重释放 // ❌ 错误3忘记 release 后要手动 delete unique_ptrint p5 make_uniqueint(42); int* raw2 p5.release(); // raw2 需要手动 delete否则泄漏 delete raw2; // ✅ 正确用 get 只是临时查看不转移所有权 unique_ptrint p6 make_uniqueint(42); int* raw3 p6.get(); // 只是查看所有权还是 p6 的 func(raw3); // 可以传给只读函数总结一、核心要点特性说明所有权独占不可拷贝只可移动创建make_uniqueT(args)释放自动离开作用域、reset()、release()访问*p、p-、p.get()自定义删除器非 new 分配的资源FILE*、socket 等性能零开销和原始指针相同头文件memory二、一句话记忆unique_ptr独占对象所有权禁止拷贝只允许移动。用make_unique创建离开作用域自动释放。需要管理非 new 资源时用自定义删除器。零开销和原始指针一样快。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Claude Code编程智能体实战:从Agent原理到工程落地 2026/9/1 10:40:09

Claude Code编程智能体实战:从Agent原理到工程落地

2026年做开发,如果还习惯把 AI 编程工具当成“聊天框 代码粘贴板”,你其实只用了 AI 大模型的一小部分价值。真正拉开效率差距的,是让 AI 以 Agent 的形态直接进入工程现场:它能读你项目的目录结构、能修改文件、能执行命令、能调…

阅读更多 →
Qwen3VL 本地部署与 LoRA 微调实战:从原理到量化推理 2026/9/1 10:40:09

Qwen3VL 本地部署与 LoRA 微调实战:从原理到量化推理

1. Qwen3VL 是什么?我们先从 VLM 说起1.1 多模态模型与 VLM 的基本概念最近两年,大模型的竞争重心已经从“纯文本对话”扩展到了“多模态理解”。所谓多模态,简单理解就是模型不再只吃文字输入,而是能同时处理图片、文字、音频、视…

阅读更多 →
50W半导体制冷片温控系统实战:从PID算法到Arduino/Python完整实现 2026/9/1 10:40:09

50W半导体制冷片温控系统实战:从PID算法到Arduino/Python完整实现

最近在折腾一些需要主动散热的电子设备时,发现传统的风冷方案在特定紧凑空间或静音要求下有些力不从心。于是把目光投向了半导体制冷片(TEC),尤其是像“YYX7MAX”这类标称功率较大(如50W)、制冷片数量多&am…

阅读更多 →
STM32F103驱动OV7670摄像头:从寄存器配置到LCD实时显示 2026/9/1 10:40:09

STM32F103驱动OV7670摄像头:从寄存器配置到LCD实时显示

简介:基于STM32F103驱动OV7670摄像头并在OLED屏上实时显示图像的开发包,适合嵌入式学习者、电子竞赛备赛者以及需要快速上手摄像头图像采集与显示项目的开发者。压缩包共101个文件,约7MB,包含20个h头文件与17个c源文件&#xff0c…

阅读更多 →
基于STM32的UWB定位源码详解:从测距到坐标解算 2026/9/1 10:40:09

基于STM32的UWB定位源码详解:从测距到坐标解算

简介:这是一份基于STM32的UWB定位系统完整源码,面向嵌入式定位应用开发者和相关专业学生,也适合毕业设计、课程设计及工业室内定位原型验证。项目覆盖DW1000芯片驱动、超宽带TOF测距、多点定位算法解算以及ESP8266 WiFi模块数据上报的完整技术…

阅读更多 →
从晶体生长到精密加工:培育彩色蓝宝石标志性产品的技术实现 2026/9/1 10:37:08

从晶体生长到精密加工:培育彩色蓝宝石标志性产品的技术实现

这次我们来看一个名为“培育彩色蓝宝石子弹头标志性产品”的项目。从标题来看,这并非一个常见的开源AI模型或软件工具,而更像是一个涉及材料科学、珠宝设计或工业制造领域的特定产品研发项目。其核心很可能围绕“培育彩色蓝宝石”这种人造宝石材料&#…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞