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

【c++面向对象编程】第30篇:RAII与智能指针(一):auto_ptr的缺陷与unique_ptr

目录一、一个手动管理的痛点二、RAII 核心思想三、auto_ptrC98 的尝试与缺陷auto_ptr 的核心缺陷四、unique_ptr真正的独占式智能指针基本用法常用成员函数五、unique_ptr 与数组六、自定义删除器七、make_uniqueC14八、完整例子对比手动管理 vs unique_ptr九、unique_ptr 的常见误区误区1试图拷贝 unique_ptr误区2用 get() 获取裸指针后 delete误区3release() 后忘记释放内存误区4用裸指针重新赋值 unique_ptr十、这一篇的收获一、一个手动管理的痛点cppvoid riskyFunction() { int* p new int[1000]; // ... 做一些操作 ... if (something_wrong) { return; // 忘记 delete[] → 内存泄漏 } delete[] p; }问题在于delete很容易被遗忘特别是在有多个返回路径或异常抛出的情况下。RAII 解决方案让对象自动管理资源。cppvoid safeFunction() { vectorint v(1000); // 内存由 vector 管理 // ... 无论怎么返回v 的析构函数都会释放内存 }这就是 RAII 的精髓将资源的生命周期与对象的生命周期绑定。二、RAII 核心思想RAIIResource Acquisition Is Initialization的含义获取资源即初始化在构造函数中获取资源分配内存、打开文件、加锁等释放资源即析构在析构函数中释放资源释放内存、关闭文件、解锁等对象生命周期结束资源自动释放cppclass FileHandle { FILE* file; public: FileHandle(const char* filename) { file fopen(filename, r); } ~FileHandle() { if (file) fclose(file); // 自动关闭 } // 禁止拷贝后面解释 };只要FileHandle对象存在文件就是打开的对象销毁时文件自动关闭。不再需要手动调用fclose。三、auto_ptrC98 的尝试与缺陷C98 引入了auto_ptr试图实现 RAII 式的动态内存管理。cpp#include memory // auto_ptr 在这里 auto_ptrint p1(new int(42)); auto_ptrint p2 p1; // 看起来像拷贝但实际是转移所有权 // 此时 p1 已经变成 nullptrauto_ptr 的核心缺陷auto_ptr的拷贝构造函数和赋值运算符会转移所有权被复制的指针变成nullptr。这违反了正常的拷贝语义cppauto_ptrstring p1(new string(hello)); auto_ptrstring p2(p1); // p1 被置空 cout *p1; // 崩溃p1 已经是空指针更严重的问题auto_ptr不能用于 STL 容器因为容器要求元素有正常的拷贝行为。cppvectorauto_ptrint vec; // 能编译但绝对不要这样做 vec.push_back(auto_ptrint(new int(1))); vec.push_back(auto_ptrint(new int(2))); // 容器内部拷贝时所有权转移导致原始指针变空非常容易出错结论auto_ptr在 C11 中被正式废弃deprecatedC17 中已移除。永远不要用它。四、unique_ptr真正的独占式智能指针unique_ptr是auto_ptr的现代替代品核心特点独占所有权一个资源只能被一个unique_ptr拥有禁止拷贝拷贝构造函数和拷贝赋值被delete支持移动通过移动语义转移所有权零开销与裸指针大小相同通常 8 字节没有额外虚函数开销基本用法cpp#include memory using namespace std; unique_ptrint p1(new int(42)); unique_ptrint p2 move(p1); // 转移所有权p1 变为空 if (!p1) cout p1 为空 endl; cout *p2 endl; // 42 // ❌ 编译错误不能拷贝 // unique_ptrint p3 p1;常用成员函数函数作用get()返回裸指针不转移所有权release()释放所有权返回裸指针不 deletereset()释放当前对象并可选接管新指针reset(nullptr)释放当前对象置空operator bool()判断是否为空cppunique_ptrint p(new int(100)); int* raw p.release(); // p 放弃所有权变为 nullptr delete raw; // 需要手动释放 p.reset(new int(200)); // 释放旧对象接管新对象 p.reset(); // 释放对象置空 if (p) { cout p 非空 endl; }五、unique_ptr 与数组unique_ptr支持数组版本使用T[]模板参数cppunique_ptrint[] arr(new int[10]); arr[0] 42; // 可以用下标访问 // 不需要手动 delete[] // 离开作用域时自动调用 delete[]注意unique_ptrT用deleteunique_ptrT[]用delete[]编译器会自动选择正确的释放方式。六、自定义删除器有时资源不是new分配的如fopen、malloc、socket需要自定义释放方式。cpp#include memory #include cstdio using namespace std; // 自定义删除器函数对象 struct FileDeleter { void operator()(FILE* f) const { if (f) { fclose(f); cout 文件已关闭 endl; } } }; int main() { unique_ptrFILE, FileDeleter file(fopen(test.txt, r)); // 自动调用 fclose不需要手动关闭 return 0; }使用 lambda 作为删除器更简洁cppauto deleter [](FILE* f) { if (f) fclose(f); }; unique_ptrFILE, decltype(deleter) file(fopen(test.txt, r), deleter);七、make_uniqueC14C14 提供了make_unique更安全、更高效地创建unique_ptrcpp// C11 方式 unique_ptrint p1(new int(42)); // C14 方式推荐 auto p2 make_uniqueint(42); auto p3 make_uniqueint[](10); // 数组版本优势异常安全避免new和unique_ptr构造之间的空隙代码更简洁减少重复类型名八、完整例子对比手动管理 vs unique_ptrcpp#include iostream #include memory #include vector using namespace std; class Resource { int id; public: Resource(int i) : id(i) { cout Resource id 已获取 endl; } ~Resource() { cout Resource id 已释放 endl; } void use() { cout 使用 Resource id endl; } }; // 手动管理容易出错 void manualManagement() { cout 手动管理 endl; Resource* r new Resource(1); r-use(); // 忘记 delete → 内存泄漏 // 即使记得如果中间抛异常也会泄漏 } // unique_ptr 管理自动释放 void uniquePtrManagement() { cout \n unique_ptr 管理 endl; auto r make_uniqueResource(2); r-use(); // 函数结束自动释放 } // 所有权转移示例 void ownershipTransfer() { cout \n 所有权转移 endl; unique_ptrResource p1 make_uniqueResource(3); cout p1 拥有资源 endl; unique_ptrResource p2 move(p1); // 转移所有权 cout 转移后: p1 (p1 ? 非空 : 空) endl; cout p2 拥有资源 endl; p2-use(); // p2 释放资源 } // 放入容器 void containerUsage() { cout \n 放入容器 endl; vectorunique_ptrResource vec; vec.push_back(make_uniqueResource(4)); vec.push_back(make_uniqueResource(5)); vec.push_back(make_uniqueResource(6)); for (auto p : vec) { p-use(); } // 容器销毁时所有 unique_ptr 自动释放资源 } int main() { // manualManagement(); // 会泄漏不运行 uniquePtrManagement(); ownershipTransfer(); containerUsage(); return 0; }输出text unique_ptr 管理 Resource 2 已获取 使用 Resource 2 Resource 2 已释放 所有权转移 Resource 3 已获取 p1 拥有资源 转移后: p1 空 p2 拥有资源 使用 Resource 3 Resource 3 已释放 放入容器 Resource 4 已获取 Resource 5 已获取 Resource 6 已获取 使用 Resource 4 使用 Resource 5 使用 Resource 6 Resource 6 已释放 Resource 5 已释放 Resource 4 已释放九、unique_ptr 的常见误区误区1试图拷贝 unique_ptrcppunique_ptrint p1(new int(5)); unique_ptrint p2 p1; // ❌ 编译错误误区2用get()获取裸指针后 deletecppunique_ptrint p(new int(5)); int* raw p.get(); delete raw; // ❌ 重复释放p 析构时会再次 delete误区3release()后忘记释放内存cppauto p make_uniqueint(42); int* raw p.release(); // p 放弃所有权 // 没有 delete raw → 内存泄漏误区4用裸指针重新赋值unique_ptrcppunique_ptrint p(new int(5)); p new int(6); // ❌ 编译错误不支持隐式转换 p.reset(new int(6)); // ✅ 正确十、这一篇的收获你现在应该理解RAII构造函数获取资源析构函数释放资源对象生命周期绑定资源auto_ptr缺陷拷贝转移所有权违反直觉不能用于容器已被废弃unique_ptr独占所有权禁止拷贝支持移动零开销常用操作make_unique、reset、release、get、移动语义转移所有权数组支持unique_ptrT[]自动调用delete[]自定义删除器可管理fopen、malloc等非new资源 小作业写一个unique_ptrFILE, CustomDeleter管理打开的文件验证函数返回或异常时文件自动关闭。然后尝试写一段代码演示move转移所有权后原指针变为空。下一篇预告第31篇《智能指针二shared_ptr与weak_ptr——循环引用破解》——多个对象需要共享同一资源时用shared_ptr。但两个shared_ptr互相引用会导致资源永远无法释放weak_ptr正是破解循环引用的利器。下篇详解。

相关文章:

【c++面向对象编程】第30篇:RAII与智能指针(一):auto_ptr的缺陷与unique_ptr

目录 一、一个手动管理的痛点 二、RAII 核心思想 三、auto_ptr:C98 的尝试与缺陷 auto_ptr 的核心缺陷 四、unique_ptr:真正的独占式智能指针 基本用法 常用成员函数 五、unique_ptr 与数组 六、自定义删除器 七、make_unique(C14&a…...

StreamFX终极指南:5个简单技巧打造专业级OBS直播画面

StreamFX终极指南:5个简单技巧打造专业级OBS直播画面 【免费下载链接】obs-StreamFX StreamFX is a plugin for OBS Studio which adds many new effects, filters, sources, transitions and encoders! Be it 3D Transform, Blur, complex Masking, or even custom…...

Borderless Gaming终极指南:如何轻松实现无边框游戏窗口管理

Borderless Gaming终极指南:如何轻松实现无边框游戏窗口管理 【免费下载链接】Borderless-Gaming Play your favorite games in a borderless window; no more time consuming alt-tabs. 项目地址: https://gitcode.com/gh_mirrors/bo/Borderless-Gaming 你…...

CefFlashBrowser完整使用手册:在2025年拯救你的Flash游戏与存档

CefFlashBrowser完整使用手册:在2025年拯救你的Flash游戏与存档 【免费下载链接】CefFlashBrowser Flash浏览器 / Flash Browser 项目地址: https://gitcode.com/gh_mirrors/ce/CefFlashBrowser 还记得那些曾经让你沉迷的Flash小游戏吗?&#x1f…...

Unity 5.6移动VR开发与单通道渲染优化指南

1. Unity 5.6移动VR开发环境配置1.1 Daydream原生支持解析Unity 5.6首次实现了对Daydream平台的原生支持,这标志着移动VR开发进入新阶段。与传统的插件式集成不同,原生支持直接内置于引擎核心,带来三个显著优势:性能提升&#xff…...

GitHub下载太慢?3分钟学会Fast-GitHub加速插件的终极解决方案

GitHub下载太慢?3分钟学会Fast-GitHub加速插件的终极解决方案 【免费下载链接】Fast-GitHub 国内Github下载很慢,用上了这个插件后,下载速度嗖嗖嗖的~! 项目地址: https://gitcode.com/gh_mirrors/fa/Fast-GitHub 作为一名…...

鸿蒙 HarmonyOS 6 | Pura X Max 鸿蒙原生适配 07:页面边距和最大内容宽度控制

前言 Pura X Max 展开态最容易出现的一类问题,是内容区域被直接撑满整屏。 列表页还能通过双列、三列解决一部分空间问题,阅读页、表单页、详情页就没这么简单了。标题、正文、输入框、说明文字一旦横向拉得太宽,用户读起来会很累。尤其是详情…...

嵌入式Linux CPU频率固定:原理、方法与ElfBoard实战

1. 项目概述:为什么需要固定CPU频率?在嵌入式开发领域,尤其是像ElfBoard这样的ARM开发板上进行应用开发或性能调优时,CPU频率的动态调整(DVFS,动态电压频率调整)有时会成为一把双刃剑。对于追求…...

基于RAG与向量数据库的智能知识库系统构建实战

1. 项目概述:当AI成为你的“第二大脑”最近在折腾一个挺有意思的开源项目,叫IIMS-By-AI。这个名字乍一看有点唬人,IIMS是“Intelligent Information Management System”的缩写,翻译过来就是“智能信息管理系统”。但它的核心玩法…...

如何用VR-Reversal在普通屏幕上观看VR视频:3分钟免费转换指南

如何用VR-Reversal在普通屏幕上观看VR视频:3分钟免费转换指南 【免费下载链接】VR-reversal VR-Reversal - Player for conversion of 3D video to 2D with optional saving of head tracking data and rendering out of 2D copies. 项目地址: https://gitcode.co…...

Rust嵌入式开发实战:开源机械爪控制库openclaw-rs架构解析与应用

1. 项目概述:当Rust遇上开源机械爪最近在逛GitHub的时候,偶然发现了一个挺有意思的项目——neul-labs/openclaw-rs。光看名字,你大概能猜到它是个用Rust语言写的、跟机械爪(Claw)相关的开源项目。没错,这正…...

WeChatExporter:将你的数字记忆转化为永恒的数字档案

WeChatExporter:将你的数字记忆转化为永恒的数字档案 【免费下载链接】WeChatExporter 一个可以快速导出、查看你的微信聊天记录的工具 项目地址: https://gitcode.com/gh_mirrors/wec/WeChatExporter 你是否曾有过这样的经历?深夜翻看旧手机&…...

5分钟快速上手Figma中文界面:设计师必备的终极汉化插件指南

5分钟快速上手Figma中文界面:设计师必备的终极汉化插件指南 【免费下载链接】figmaCN 中文 Figma 插件,设计师人工翻译校验 项目地址: https://gitcode.com/gh_mirrors/fi/figmaCN 还在为Figma全英文界面而苦恼吗?FigmaCN中文插件是你…...

别再手动点选了!用Python脚本5分钟搞定Abaqus批量加载节点力(附完整代码)

Python自动化赋能Abaqus:高效批量加载节点力的工程实践 在有限元分析领域,Abaqus作为行业标杆软件,其强大的计算能力与灵活的二次开发接口深受工程师青睐。然而,当面对需要为数百甚至上千个节点分别施加不同载荷的复杂工况时&…...

单例模式深度解析:从基础实现到生产级避坑指南

1. 单例模式:为什么它既是基石又是“坑”在软件开发的江湖里,单例模式(Singleton Pattern)的名号,几乎无人不知。它被写进教科书,是设计模式中最容易理解、也最常被提及的模式之一。但有趣的是,…...

开源可观测性平台SigNoz:一体化监控与分布式链路追踪实战

1. 项目概述:从可观测性痛点出发,为什么我们需要SigNoz在云原生和微服务架构成为主流的今天,一个应用可能由数十甚至上百个服务组成,它们分布在不同的容器、节点甚至云区域中。当用户反馈“页面加载慢”或“功能报错”时&#xff…...

LabVIEW集成Python虚拟环境:基于Conda的隔离部署与工程实践

1. 项目概述:当LabVIEW遇上Python虚拟环境如果你是一名LabVIEW开发者,最近是不是经常听到团队里讨论Python?或者你自己也遇到了这样的场景:一个复杂的算法,用G语言实现起来异常繁琐,但Python社区里却有现成…...

体验Taotoken官方价折扣与活动价带来的实际成本节省

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 体验Taotoken官方价折扣与活动价带来的实际成本节省 对于开发者与团队而言,大模型API的调用成本是项目预算中不可忽视的…...

EvoAgentX智能体开发框架:模块化架构与进化引擎解析

1. 项目概述:一个面向未来的智能体开发框架最近在探索智能体(Agent)开发领域时,我遇到了一个名为“EvoAgentX”的项目。这个名字本身就很有意思,“Evo”暗示着进化,“AgentX”则指向了智能体及其无限的可能…...

西安小程序制作优质服务推荐

在西安,小程序制作已成为众多企业实现数字化转型的核心一步。企业在这个领域的选择尤为重要,因为市场上的服务供应商数量庞大、难以判断其服务质量。因此专业背景、以往案例以及客户评价,这些都能够反映出公司的整体实力。还有,成…...

Web架构师工具箱:从工程化实践到现代化Web开发全流程

1. 项目概述:一个Web架构师的工具箱最近在GitHub上看到一个挺有意思的项目,叫choppawave-beep/web-architect。光看这个名字,你可能会有点摸不着头脑,choppawave-beep像是个用户名,而web-architect则直白地指向“Web架…...

为ClaudeCode配置Taotoken作为稳定可靠的API供应商

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 为ClaudeCode配置Taotoken作为稳定可靠的API供应商 Claude Code 是一款广受开发者欢迎的编程助手工具,它依赖于后端的大…...

出口土耳其:关键注意事项与避坑指南

与土耳其贸易需重点关注收汇安全、海关政策及单证认证。掌握即期信用证规则、海关拍卖时限及使馆认证要求,是防范货款与货物风险的关键。一、收汇风险防范土耳其商人常要求赊账或开具空头支票,部分还以个人财产抵押开具汇票,此类方式风险极高…...

中小团队如何通过Taotoken统一管理多个AI项目的API成本

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 中小团队如何通过Taotoken统一管理多个AI项目的API成本 应用场景类,面向同时进行多个AI应用探索或开发的中小团队技术管…...

LLM应用开发资源导航:从Awesome List到实战项目构建

1. 项目概述:当“Awesome”遇见LLM应用如果你最近在GitHub上逛过,或者对大型语言模型(LLM)的应用开发感兴趣,那么“Shubhamsaboo/awesome-llm-apps”这个仓库大概率已经躺在你的浏览器书签或者GitHub星标列表里了。它不…...

基于RT-Thread与N32G457的工业UART网关设计与实现

1. 项目概述与核心价值最近在做一个工业数据采集的项目,现场有十几台不同品牌、不同协议的串口设备,PLC、仪表、传感器什么都有,它们的数据都需要汇总到一台中心服务器上。最头疼的是,这些设备分布在车间各处,拉线成本…...

OpenClaw自动化配置实战:从入门到精通,打造高效工作流

1. 项目概述与核心价值最近在折腾开源自动化工具,发现了一个宝藏仓库:ShuyuZ1999/awesome-openclaw-configs。这个项目乍一看名字有点长,但核心价值非常明确——它是一个专门为开源自动化工具OpenClaw收集、整理和分享高质量配置文件的集合。…...

5.【Python】Python3 运算符

第一步:分析与整理 运算符1. 什么是运算符? 运算符用于执行算术、比较、逻辑等操作。操作数是参与运算的值。例如 4 5 9 中,4 和 5 是操作数, 是运算符。 Python 支持以下运算符类型: 算术运算符比较(关系…...

晶圆为何是圆形而芯片是方形?揭秘半导体制造的工程智慧

1. 项目概述:一个看似简单却充满工程智慧的谜题“为什么晶圆是圆的,而芯片是方的?” 这个问题,乍一听像是半导体行业里一个有趣的脑筋急转弯,但它背后却串联起了从材料科学、物理化学到精密制造、经济学乃至数学几何的…...

基于MCP协议实现AI安全访问MongoDB:架构、部署与安全实践

1. 项目概述与核心价值最近在折腾AI应用开发,特别是想让大语言模型(LLM)能直接操作数据库,比如MongoDB。这听起来很酷,对吧?想象一下,你直接告诉AI助手“帮我查一下上个月销量最高的产品”&…...