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

std::unique_ptr 复习

一、std::unique_ptr 概述

std::unique_ptr是 C++11 引入的智能指针,它独占对所管理对象的所有权,即同一时间只能有一个unique_ptr指向同一个对象。当unique_ptr离开作用域或被重置时,它会自动释放所管理的对象。

核心特性

  • 独占所有权:一个对象只能被一个unique_ptr拥有
  • 移动语义:通过移动操作转移所有权,禁止拷贝
  • 零开销:与裸指针相比,几乎没有额外的内存开销
  • 支持数组:有专门的特化版本管理数组
  • 自定义删除器:可以指定自定义的删除逻辑

二、基本用法

1. 头文件与命名空间

#include<memory>// 包含智能指针头文件usingnamespacestd;// 可选,为了代码简洁

2. 构造与初始化

(1)默认构造
unique_ptr<int>ptr1;// 空的 unique_ptr
(2)通过裸指针构造
unique_ptr<int>ptr2(newint(10));// 管理一个 int 对象
(3)通过 make_unique 构造(C++14+)
autoptr3=make_unique<int>(20);// 更安全的构造方式autoptr4=make_unique<string>("Hello");// 构造字符串autoptr5=make_unique<int[]>(5);// 构造数组
(4)通过移动构造
unique_ptr<int>ptr6(newint(30));unique_ptr<int>ptr7(std::move(ptr6));// 转移所有权,ptr6 变为空

3. 基本操作

(1)访问所管理的对象
unique_ptr<int>ptr(newint(10));// 通过 operator* 访问对象*ptr=20;cout<<*ptr<<endl;// 输出:20// 通过 operator-> 访问对象的成员unique_ptr<string>strPtr=make_unique<string>("Hello");cout<<strPtr->size()<<endl;// 输出:5
(2)获取原始指针
unique_ptr<int>ptr(newint(10));int*rawPtr=ptr.get();// 获取原始指针cout<<*rawPtr<<endl;// 输出:10
(3)释放所有权
unique_ptr<int>ptr(newint(10));int*rawPtr=ptr.release();// 释放所有权,ptr 变为空// 此时需要手动释放内存deleterawPtr;
(4)重置
unique_ptr<int>ptr(newint(10));ptr.reset(newint(20));// 释放旧资源,管理新资源ptr.reset();// 释放资源,ptr 变为空
(5)检查是否为空
unique_ptr<int>ptr1;if(!ptr1){cout<<"ptr1 is empty"<<endl;}unique_ptr<int>ptr2(newint(10));if(ptr2){cout<<"ptr2 is not empty"<<endl;}

4. 数组管理

unique_ptr有专门的数组特化版本,用于管理动态数组:

// 构造数组unique_ptr<int[]>arrPtr(newint[5]);unique_ptr<int[]>arrPtr2=make_unique<int[]>(5);// 访问数组元素for(inti=0;i<5;++i){arrPtr[i]=i;cout<<arrPtr[i]<<" ";}cout<<endl;// 输出:0 1 2 3 4// 自动使用 delete[] 释放数组

三、移动语义

unique_ptr禁止拷贝,但支持移动语义,通过std::move转移所有权:

1. 移动构造

unique_ptr<int>ptr1(newint(10));unique_ptr<int>ptr2(std::move(ptr1));// 转移所有权// 此时 ptr1 为空,ptr2 拥有资源if(!ptr1){cout<<"ptr1 is empty"<<endl;}if(ptr2){cout<<"ptr2 is not empty, value: "<<*ptr2<<endl;}

2. 移动赋值

unique_ptr<int>ptr1(newint(10));unique_ptr<int>ptr2;ptr2=std::move(ptr1);// 转移所有权// 此时 ptr1 为空,ptr2 拥有资源

3. 作为函数返回值

unique_ptr<int>createInt(){returnunique_ptr<int>(newint(42));// 或使用 make_unique// return make_unique<int>(42);}unique_ptr<int>ptr=createInt();// 自动移动,无需显式 std::movecout<<*ptr<<endl;// 输出:42

4. 与 STL 容器配合使用

vector<unique_ptr<int>>vec;// 添加元素(必须使用移动)vec.push_back(make_unique<int>(10));vec.push_back(std::move(ptr));// 遍历容器for(constauto&p:vec){cout<<*p<<" ";}cout<<endl;

四、自定义删除器

unique_ptr允许指定自定义删除器,用于处理特殊的资源释放逻辑:

1. 函数对象作为删除器

// 定义删除器结构体structFileDeleter{voidoperator()(FILE*fp){if(fp){fclose(fp);cout<<"File closed"<<endl;}}};// 使用自定义删除器unique_ptr<FILE,FileDeleter>filePtr(fopen("test.txt","w"));if(filePtr){fprintf(filePtr.get(),"Hello, World!");}// 离开作用域时,会自动调用 FileDeleter::operator() 关闭文件

2. Lambda 表达式作为删除器

// 使用 lambda 作为删除器autodeleter=[](int*p){cout<<"Deleting int: "<<*p<<endl;deletep;};unique_ptr<int,decltype(deleter)>ptr(newint(42),deleter);

3. 管理非内存资源

// 管理套接字structSocketDeleter{voidoperator()(int*sockfd){if(*sockfd!=-1){close(*sockfd);cout<<"Socket closed"<<endl;}deletesockfd;}};unique_ptr<int,SocketDeleter>sockPtr(newint(socket(AF_INET,SOCK_STREAM,0)));

五、std::unique_ptr 与其他智能指针的对比

特性std::unique_ptrstd::shared_ptrstd::auto_ptr(已废弃)
所有权独占共享独占,拷贝转移
拷贝操作禁用支持(增加引用计数)支持(转移所有权)
移动操作支持支持不支持(仅拷贝)
内存开销无额外开销引用计数开销无额外开销
线程安全否(但移动操作线程安全)引用计数线程安全
管理数组支持(特化版本)支持(需自定义删除器)不支持
自定义删除器支持支持不支持

六、最佳实践

1. 优先使用 make_unique

// 推荐autoptr=make_unique<int>(42);// 不推荐unique_ptr<int>ptr(newint(42));

原因

  • 更简洁,代码可读性更高
  • 异常安全:避免内存泄漏
  • 统一的初始化语法

2. 避免裸指针与智能指针混用

// 错误:裸指针与智能指针混用int*rawPtr=newint(42);unique_ptr<int>smartPtr(rawPtr);// ...deleterawPtr;// 重复释放,导致未定义行为

3. 正确处理所有权转移

// 正确:使用移动语义转移所有权unique_ptr<int>ptr1=make_unique<int>(42);unique_ptr<int>ptr2=std::move(ptr1);// 明确转移所有权// 错误:尝试拷贝(编译失败)// unique_ptr<int> ptr3 = ptr2; // 编译错误

4. 作为函数参数和返回值

// 作为参数(传递引用或移动)voidprocess(unique_ptr<int>&ptr){// 使用 ptr}voidtakeOwnership(unique_ptr<int>ptr){// 接管所有权}// 作为返回值unique_ptr<int>createPtr(){returnmake_unique<int>(42);// 自动移动}

5. 与 STL 容器配合

vector<unique_ptr<int>>vec;// 正确:使用 emplace_back 或 push_back(std::move())vec.emplace_back(make_unique<int>(10));unique_ptr<int>ptr=make_unique<int>(20);vec.push_back(std::move(ptr));// 错误:尝试拷贝(编译失败)// vec.push_back(ptr); // 编译错误

七、代码示例

示例 1:基本用法

#include<iostream>#include<memory>intmain(){// 使用 make_unique 构造autoptr=std::make_unique<int>(42);std::cout<<"Value: "<<*ptr<<std::endl;// 重置ptr.reset(newint(100));std::cout<<"New value: "<<*ptr<<std::endl;// 移动语义autoptr2=std::move(ptr);if(!ptr){std::cout<<"ptr is now empty"<<std::endl;}std::cout<<"ptr2 value: "<<*ptr2<<std::endl;return0;}

示例 2:管理数组

#include<iostream>#include<memory>intmain(){// 管理数组autoarr=std::make_unique<int[]>(5);// 初始化数组for(inti=0;i<5;++i){arr[i]=i*10;}// 打印数组for(inti=0;i<5;++i){std::cout<<arr[i]<<" ";}std::cout<<std::endl;return0;// 自动释放数组}

示例 3:自定义删除器

#include<iostream>#include<memory>intmain(){// 自定义删除器autodeleter=[](int*p){std::cout<<"Deleting: "<<*p<<std::endl;deletep;};// 使用自定义删除器std::unique_ptr<int,decltype(deleter)>ptr(newint(42),deleter);std::cout<<"Value: "<<*ptr<<std::endl;return0;// 自动调用自定义删除器}

八、总结

std::unique_ptr是 C++11 引入的一种轻量级智能指针,它通过独占所有权和移动语义,提供了一种安全、高效的内存管理方式。它的主要优势包括:

  1. 零开销:与裸指针相比几乎没有额外的内存开销
  2. 独占所有权:避免了资源的重复释放
  3. 移动语义:明确的所有权转移,避免了意外的拷贝
  4. 支持数组:有专门的特化版本用于管理数组
  5. 自定义删除器:可以处理特殊的资源释放逻辑

std::unique_ptr是默认的智能指针选择,只有当需要共享所有权时,才使用std::shared_ptr

http://www.jsqmd.com/news/611912/

相关文章:

  • 3秒获取百度网盘提取码:baidupankey工具让资源获取效率提升280%
  • 罗振宇、张鹏、王力行等齐聚一堂,AIFUT大会Day1上午场的全面总结来了。
  • 从“雇前可信”到“在职可控”:江湖背调如何定义国内最准确的背调数据标准
  • RTX 4090专属视觉工具:Qwen2.5-VL-7B-Instruct部署与使用全指南
  • Redis位图实战:用BITFIELD实现高效用户签到系统(附完整代码)
  • smart-doc实战:一键生成Postman集合与对接Torna文档平台完整流程
  • Perforce 静态分析现已正式支持 Rust语言
  • OpenClaw安全方案:百川2-13B-4bits本地模型处理敏感数据实战
  • 制造业企业怎样用好数据智能?聚焦排产、质检与能耗三大场景
  • 通义千问3-4B量化技巧:GGUF-Q4压缩后性能保持指南
  • Pixel Dimension Fissioner 教育领域创新:动态生成数据结构与算法可视化图
  • 比特学习编程C语言
  • 你的终端神器之Oh My Zsh汤
  • 轻松调整PPT比例的3步技巧,Rust 与 传统语言:现代系统编程的深度对比。
  • SGLang-v0.5.6应用:快速搭建智能客服对话系统
  • 效果展示:TranslateGemma翻译质量实测,法律技术文档翻译精准流畅
  • Qwen3-0.6B-FP8集成至Node.js服务:构建全栈JavaScript智能应用
  • 忍者像素绘卷部署案例:中小企业IP视觉化工具——微信小程序+私有化部署方案
  • 【数据积木·数据体系篇】四集之聚集篇(番外篇):指标、维度:从汉语拼音的“声韵组合”到数据世界的“语义表达”
  • 实验室DIY:用氢氧化钠溶液快速去除MOSFET封装(学生党必备)
  • 【Solar应急预警】开源智能体OpenClaw(小龙虾)内网暴露风险剖析与多维排查指南
  • 分享 种 .NET 桌面应用程序自动更新解决方案诼
  • Youtu-Parsing保姆级入门:上传图片自动识别文字、表格、公式
  • SeqGPT创意写作助手:激发创作灵感的5种用法
  • 2026年全域聚合支付前景如何?一文揭秘!
  • Cosmos-Reason1-7B效果展示:对‘为什么这个递归会栈溢出’提问,输出调用深度热力图分析
  • OpenClaw语音交互:Qwen3-4B对接语音输入输出模块
  • 使用Alpine配置WSL ssh门户还
  • 从段错误到 2300万OPS:我如何为KV存储重构内存池
  • CoTracker算法深度拆解:Transformer时空注意力如何实现密集点联合追踪