std::unique_ptr 复习

张开发
2026/4/9 8:32:18 15 分钟阅读

分享文章

std::unique_ptr 复习
一、std::unique_ptr 概述std::unique_ptr是 C11 引入的智能指针它独占对所管理对象的所有权即同一时间只能有一个unique_ptr指向同一个对象。当unique_ptr离开作用域或被重置时它会自动释放所管理的对象。核心特性独占所有权一个对象只能被一个unique_ptr拥有移动语义通过移动操作转移所有权禁止拷贝零开销与裸指针相比几乎没有额外的内存开销支持数组有专门的特化版本管理数组自定义删除器可以指定自定义的删除逻辑二、基本用法1. 头文件与命名空间#includememory// 包含智能指针头文件usingnamespacestd;// 可选为了代码简洁2. 构造与初始化1默认构造unique_ptrintptr1;// 空的 unique_ptr2通过裸指针构造unique_ptrintptr2(newint(10));// 管理一个 int 对象3通过 make_unique 构造C14autoptr3make_uniqueint(20);// 更安全的构造方式autoptr4make_uniquestring(Hello);// 构造字符串autoptr5make_uniqueint[](5);// 构造数组4通过移动构造unique_ptrintptr6(newint(30));unique_ptrintptr7(std::move(ptr6));// 转移所有权ptr6 变为空3. 基本操作1访问所管理的对象unique_ptrintptr(newint(10));// 通过 operator* 访问对象*ptr20;cout*ptrendl;// 输出20// 通过 operator- 访问对象的成员unique_ptrstringstrPtrmake_uniquestring(Hello);coutstrPtr-size()endl;// 输出52获取原始指针unique_ptrintptr(newint(10));int*rawPtrptr.get();// 获取原始指针cout*rawPtrendl;// 输出103释放所有权unique_ptrintptr(newint(10));int*rawPtrptr.release();// 释放所有权ptr 变为空// 此时需要手动释放内存deleterawPtr;4重置unique_ptrintptr(newint(10));ptr.reset(newint(20));// 释放旧资源管理新资源ptr.reset();// 释放资源ptr 变为空5检查是否为空unique_ptrintptr1;if(!ptr1){coutptr1 is emptyendl;}unique_ptrintptr2(newint(10));if(ptr2){coutptr2 is not emptyendl;}4. 数组管理unique_ptr有专门的数组特化版本用于管理动态数组// 构造数组unique_ptrint[]arrPtr(newint[5]);unique_ptrint[]arrPtr2make_uniqueint[](5);// 访问数组元素for(inti0;i5;i){arrPtr[i]i;coutarrPtr[i] ;}coutendl;// 输出0 1 2 3 4// 自动使用 delete[] 释放数组三、移动语义unique_ptr禁止拷贝但支持移动语义通过std::move转移所有权1. 移动构造unique_ptrintptr1(newint(10));unique_ptrintptr2(std::move(ptr1));// 转移所有权// 此时 ptr1 为空ptr2 拥有资源if(!ptr1){coutptr1 is emptyendl;}if(ptr2){coutptr2 is not empty, value: *ptr2endl;}2. 移动赋值unique_ptrintptr1(newint(10));unique_ptrintptr2;ptr2std::move(ptr1);// 转移所有权// 此时 ptr1 为空ptr2 拥有资源3. 作为函数返回值unique_ptrintcreateInt(){returnunique_ptrint(newint(42));// 或使用 make_unique// return make_uniqueint(42);}unique_ptrintptrcreateInt();// 自动移动无需显式 std::movecout*ptrendl;// 输出424. 与 STL 容器配合使用vectorunique_ptrintvec;// 添加元素必须使用移动vec.push_back(make_uniqueint(10));vec.push_back(std::move(ptr));// 遍历容器for(constautop:vec){cout*p ;}coutendl;四、自定义删除器unique_ptr允许指定自定义删除器用于处理特殊的资源释放逻辑1. 函数对象作为删除器// 定义删除器结构体structFileDeleter{voidoperator()(FILE*fp){if(fp){fclose(fp);coutFile closedendl;}}};// 使用自定义删除器unique_ptrFILE,FileDeleterfilePtr(fopen(test.txt,w));if(filePtr){fprintf(filePtr.get(),Hello, World!);}// 离开作用域时会自动调用 FileDeleter::operator() 关闭文件2. Lambda 表达式作为删除器// 使用 lambda 作为删除器autodeleter[](int*p){coutDeleting int: *pendl;deletep;};unique_ptrint,decltype(deleter)ptr(newint(42),deleter);3. 管理非内存资源// 管理套接字structSocketDeleter{voidoperator()(int*sockfd){if(*sockfd!-1){close(*sockfd);coutSocket closedendl;}deletesockfd;}};unique_ptrint,SocketDeletersockPtr(newint(socket(AF_INET,SOCK_STREAM,0)));五、std::unique_ptr 与其他智能指针的对比特性std::unique_ptrstd::shared_ptrstd::auto_ptr已废弃所有权独占共享独占拷贝转移拷贝操作禁用支持增加引用计数支持转移所有权移动操作支持支持不支持仅拷贝内存开销无额外开销引用计数开销无额外开销线程安全否但移动操作线程安全引用计数线程安全否管理数组支持特化版本支持需自定义删除器不支持自定义删除器支持支持不支持六、最佳实践1. 优先使用 make_unique// 推荐autoptrmake_uniqueint(42);// 不推荐unique_ptrintptr(newint(42));原因更简洁代码可读性更高异常安全避免内存泄漏统一的初始化语法2. 避免裸指针与智能指针混用// 错误裸指针与智能指针混用int*rawPtrnewint(42);unique_ptrintsmartPtr(rawPtr);// ...deleterawPtr;// 重复释放导致未定义行为3. 正确处理所有权转移// 正确使用移动语义转移所有权unique_ptrintptr1make_uniqueint(42);unique_ptrintptr2std::move(ptr1);// 明确转移所有权// 错误尝试拷贝编译失败// unique_ptrint ptr3 ptr2; // 编译错误4. 作为函数参数和返回值// 作为参数传递引用或移动voidprocess(unique_ptrintptr){// 使用 ptr}voidtakeOwnership(unique_ptrintptr){// 接管所有权}// 作为返回值unique_ptrintcreatePtr(){returnmake_uniqueint(42);// 自动移动}5. 与 STL 容器配合vectorunique_ptrintvec;// 正确使用 emplace_back 或 push_back(std::move())vec.emplace_back(make_uniqueint(10));unique_ptrintptrmake_uniqueint(20);vec.push_back(std::move(ptr));// 错误尝试拷贝编译失败// vec.push_back(ptr); // 编译错误七、代码示例示例 1基本用法#includeiostream#includememoryintmain(){// 使用 make_unique 构造autoptrstd::make_uniqueint(42);std::coutValue: *ptrstd::endl;// 重置ptr.reset(newint(100));std::coutNew value: *ptrstd::endl;// 移动语义autoptr2std::move(ptr);if(!ptr){std::coutptr is now emptystd::endl;}std::coutptr2 value: *ptr2std::endl;return0;}示例 2管理数组#includeiostream#includememoryintmain(){// 管理数组autoarrstd::make_uniqueint[](5);// 初始化数组for(inti0;i5;i){arr[i]i*10;}// 打印数组for(inti0;i5;i){std::coutarr[i] ;}std::coutstd::endl;return0;// 自动释放数组}示例 3自定义删除器#includeiostream#includememoryintmain(){// 自定义删除器autodeleter[](int*p){std::coutDeleting: *pstd::endl;deletep;};// 使用自定义删除器std::unique_ptrint,decltype(deleter)ptr(newint(42),deleter);std::coutValue: *ptrstd::endl;return0;// 自动调用自定义删除器}八、总结std::unique_ptr是 C11 引入的一种轻量级智能指针它通过独占所有权和移动语义提供了一种安全、高效的内存管理方式。它的主要优势包括零开销与裸指针相比几乎没有额外的内存开销独占所有权避免了资源的重复释放移动语义明确的所有权转移避免了意外的拷贝支持数组有专门的特化版本用于管理数组自定义删除器可以处理特殊的资源释放逻辑std::unique_ptr是默认的智能指针选择只有当需要共享所有权时才使用std::shared_ptr。

更多文章