(8条消息) C++ 智能指针 unique

unique_ptrC++ 11 提供的用于防止内存泄漏的智能指针中的一种实现,独享被管理对象指针所有权的智能指针。unique_ptr对象包装一个原始指针,并负责其生命周期。当该对象被销毁时,会在其析构函数中删除关联的原始指针。
unique_ptr具有->*运算符重载符,因此它可以像普通指针一样使用。
查看下面的示例:

#include <iostream>#include <memory>struct Task {    int mId;    Task(int id ) :mId(id) {        std::cout << "Task::Constructor" << std::endl;    }    ~Task() {        std::cout << "Task::Destructor" << std::endl;    }};int main(){    // 通过原始指针创建 unique_ptr 实例    std::unique_ptr<Task> taskPtr(new Task(23));    //通过 unique_ptr 访问其成员    int id = taskPtr->mId;    std::cout << id << std::endl;    return 0;}

输出:

Task::Constructor23Task::Destructor

unique_ptr <Task> 对象 taskPtr 接受原始指针作为参数。现在当main函数退出时,该对象超出作用范围就会调用其析构函数,在unique_ptr对象taskPtr 的析构函数中,会删除关联的原始指针,这样就不用专门delete Task对象了。
这样不管函数正常退出还是异常退出(由于某些异常),也会始终调用taskPtr的析构函数。因此,原始指针将始终被删除并防止内存泄漏。

unique_ptr 独享所有权

unique_ptr对象始终是关联的原始指针的唯一所有者。我们无法复制unique_ptr对象,它只能移动。
由于每个unique_ptr对象都是原始指针的唯一所有者,因此在其析构函数中它直接删除关联的指针,不需要任何参考计数。

创建一个空的 unique_ptr 对象

创建一个空的unique_ptr<int>对象,因为没有与之关联的原始指针,所以它是空的。

std::unique_ptr<int> ptr1;

检查 unique_ptr 对象是否为空

有两种方法可以检查 unique_ptr 对象是否为空或者是否有与之关联的原始指针。

// 方法1if(!ptr1)std::cout<<"ptr1 is empty"<<std::endl;// 方法2if(ptr1 == nullptr)std::cout<<"ptr1 is empty"<<std::endl;

使用原始指针创建 unique_ptr 对象

要创建非空的 unique_ptr 对象,需要在创建对象时在其构造函数中传递原始指针,即:

std::unique_ptr<Task> taskPtr(new Task(22));

不能通过赋值的方法创建对象,下面的这句是错误的

// std::unique_ptr<Task> taskPtr2 = new Task(); // 编译错误

使用 std::make_unique 创建 unique_ptr 对象 / C++14

std::make_unique<>() 是C++ 14 引入的新函数

std::unique_ptr<Task> taskPtr = std::make_unique<Task>(34);

获取被管理对象的指针

使用get()·函数获取管理对象的指针。

Task *p1 = taskPtr.get();

重置 unique_ptr 对象

在 unique_ptr 对象上调用reset()函数将重置它,即它将释放delete关联的原始指针并使unique_ptr 对象为空。

taskPtr.reset();

unique_ptr 对象不可复制

由于 unique_ptr 不可复制,只能移动。因此,我们无法通过复制构造函数或赋值运算符创建unique_ptr对象的副本。

// 编译错误 : unique_ptr 不能复制std::unique_ptr<Task> taskPtr3 = taskPtr2; // Compile error// 编译错误 : unique_ptr 不能复制taskPtr = taskPtr2; //compile error

转移 unique_ptr 对象的所有权

我们无法复制 unique_ptr 对象,但我们可以转移它们。这意味着 unique_ptr 对象可以将关联的原始指针的所有权转移到另一个 unique_ptr 对象。让我们通过一个例子来理解:

// 通过原始指针创建 taskPtr2std::unique_ptr<Task> taskPtr2(new Task(55));// 把taskPtr2中关联指针的所有权转移给taskPtr4std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);// 现在taskPtr2关联的指针为空if(taskPtr2 == nullptr)std::cout<<"taskPtr2 is  empty"<<std::endl;// taskPtr2关联指针的所有权现在转移到了taskPtr4中if(taskPtr4 != nullptr)std::cout<<"taskPtr4 is not empty"<<std::endl;// 会输出55std::cout<< taskPtr4->mId << std::endl;

std::move() 将把 taskPtr2 转换为一个右值引用。因此,调用 unique_ptr 的移动构造函数,并将关联的原始指针传输到 taskPtr4。在转移完原始指针的所有权后, taskPtr2将变为空。

释放关联的原始指针

在 unique_ptr 对象上调用 release()将释放其关联的原始指针的所有权,并返回原始指针。这里是释放所有权,并没有delete原始指针,reset()会delete原始指针。

std::unique_ptr<Task> taskPtr5(new Task(55));// 不为空if(taskPtr5 != nullptr)std::cout<<"taskPtr5 is not empty"<<std::endl;// 释放关联指针的所有权Task * ptr = taskPtr5.release();// 现在为空if(taskPtr5 == nullptr)std::cout<<"taskPtr5 is empty"<<std::endl;

完整示例程序

#include <iostream>#include <memory>struct Task {    int mId;    Task(int id ) :mId(id) {        std::cout<<"Task::Constructor"<<std::endl;    }    ~Task() {        std::cout<<"Task::Destructor"<<std::endl;    }};int main(){    // 空对象 unique_ptr    std::unique_ptr<int> ptr1;    // 检查 ptr1 是否为空    if(!ptr1)        std::cout<<"ptr1 is empty"<<std::endl;    // 检查 ptr1 是否为空    if(ptr1 == nullptr)        std::cout<<"ptr1 is empty"<<std::endl;    // 不能通过赋值初始化unique_ptr    // std::unique_ptr<Task> taskPtr2 = new Task(); // Compile Error    // 通过原始指针创建 unique_ptr    std::unique_ptr<Task> taskPtr(new Task(23));    // 检查 taskPtr 是否为空    if(taskPtr != nullptr)        std::cout<<"taskPtr is  not empty"<<std::endl;    // 访问 unique_ptr关联指针的成员    std::cout<<taskPtr->mId<<std::endl;    std::cout<<"Reset the taskPtr"<<std::endl;    // 重置 unique_ptr 为空,将删除关联的原始指针    taskPtr.reset();    // 检查是否为空 / 检查有没有关联的原始指针    if(taskPtr == nullptr)        std::cout<<"taskPtr is  empty"<<std::endl;    // 通过原始指针创建 unique_ptr    std::unique_ptr<Task> taskPtr2(new Task(55));    if(taskPtr2 != nullptr)        std::cout<<"taskPtr2 is  not empty"<<std::endl;    // unique_ptr 对象不能复制    //taskPtr = taskPtr2; //compile error    //std::unique_ptr<Task> taskPtr3 = taskPtr2;    {        // 转移所有权(把unique_ptr中的指针转移到另一个unique_ptr中)        std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);        // 转移后为空        if(taskPtr2 == nullptr)            std::cout << "taskPtr2 is  empty" << std::endl;        // 转进来后非空        if(taskPtr4 != nullptr)            std::cout<<"taskPtr4 is not empty"<<std::endl;        std::cout << taskPtr4->mId << std::endl;        //taskPtr4 超出下面这个括号的作用于将delete其关联的指针    }    std::unique_ptr<Task> taskPtr5(new Task(66));    if(taskPtr5 != nullptr)        std::cout << "taskPtr5 is not empty" << std::endl;    // 释放所有权    Task * ptr = taskPtr5.release();    if(taskPtr5 == nullptr)        std::cout << "taskPtr5 is empty" << std::endl;    std::cout << ptr->mId << std::endl;    delete ptr;    return 0;}

输出:

ptr1 is emptyptr1 is emptyTask::ConstructortaskPtr is  not empty23Reset the taskPtrTask::DestructortaskPtr is  emptyTask::ConstructortaskPtr2 is  not emptytaskPtr2 is  emptytaskPtr4 is not empty55Task::DestructorTask::ConstructortaskPtr5 is not emptytaskPtr5 is empty66Task::Destructor

总结

new出来的对象是位于堆内存上的,必须调用delete才能释放其内存。
unique_ptr 是一个装指针的容器,且拥有关联指针的唯一所有权,作为普通变量使用时系统分配对象到栈内存上,超出作用域时会自动析构,unique_ptr对象的析构函数中会delete其关联指针,这样就相当于替我们执行了delete堆内存上的对象。

成员函数 作用
reset() 重置unique_ptr为空,delete其关联的指针。
release() 不delete关联指针,并返回关联指针。释放关联指针的所有权,unique_ptr为空。
get() 仅仅返回关联指针

unique_ptr不能直接复制,必须使用std::move()转移其管理的指针,转移后原 unique_ptr 为空。std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);

创建unique_ptr对象有两种方法:

//C++11: std::unique_ptr<Task> taskPtr(new Task(23));//C++14: std::unique_ptr<Task> taskPtr = std::make_unique<Task>(34);
(0)

相关推荐

  • c++动态内存分配

    下面随笔是关于c++动态内存分配. 动态申请内存操作符 new new 类型名T(初始化参数列表) 功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值. 结果值:成功:T类型 ...

  • 现代C一文读懂智能指针

    https://m.toutiao.com/is/JHS4MVf/ 智能指针 C++11 引入了 3 个智能指针类型: std::unique_ptr<T> :独占资源所有权的指针. st ...

  •  C++11中shared

    在C++中,动态内存的管理是通过一对运算符来完成的:new,在动态内存中为对象分配空间并返回一个指向该对象的指针,可以选择对对象进行初始化:delete,接受一个动态对象的指针,销毁该对象,并释放与之 ...

  • C 11中智能指针的原理、使用、实现

    目录 理解智能指针的原理 智能指针的使用 智能指针的设计和实现 1.智能指针的作用 C 程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理.程序员自己管理堆内存可以提高了程序的 ...

  • c 11新特性之智能指针

    很多人谈到c++,说它特别难,可能有一部分就是因为c++的内存管理吧,不像java那样有虚拟机动态的管理内存,在程序运行过程中可能就会出现内存泄漏,然而这种问题其实都可以通过c++11引入的智能指针来 ...

  • C++智能指针 unique

    unique_ptr 独占所指向的对象, 同一时刻只能有一个 unique_ptr 指向给定对象(通过禁止拷贝语义, 只有移动语义来实现), 定义于 memory (非memory.h)中, 命名空间 ...

  • (19条消息) 个性化智能推荐技术研究总结

    个性化智能推荐技术研究总结 随着网络与信息技术的飞速发展,互联网为用户提供越来越多的信息和服务,用户在得到便利的同时也不得不面临大量的垃圾信息和无意义数据,即所谓的信息超载问题.面对海量的网络资源,个 ...

  • (7条消息) c、c++指针和整型的互相转换

    c语言: Noncompliant Code Example(不兼容的代码示例) The size of a pointer can be greater than the size of an in ...

  • (1条消息) 智能硬件产品经理手册

    为了帮助新从事智能硬件的产品尽快的熟悉智能硬件部产品流程,掌握各种数据,平台工具的使用方法,以及提高产品设计能力.特以智能硬件产品经历为例制定适合转型到智能一到三年的PM/PD工作手册,方便新工作. ...

  • (5条消息) (七)Fabric2.0智能合约实践

    总目录: (0) 如何利用区块链保护知识产权 (一)HyperLedger Fabric 2.0-release测试网络部署 (二)Fabric2.0 first-network 生成配置说明 (三) ...

  • (5条消息) (六)Fabric2.0 智能合约实践

    总目录: (0) 如何利用区块链保护知识产权 (一)HyperLedger Fabric 2.0-release测试网络部署 (二)Fabric2.0 first-network 生成配置说明 (三) ...

  • (5条消息) (五)Fabric2.0 智能合约实践

    目录 2.1 安装以及定义智能合约 2.1.1 打包合约 2.1.2 部署合约到节点 2.1.3 当前组织同意合约定义 2.1.4 检查合约定义是否满足策略 2.1.5 提交合约 2.1.6 查看节点 ...

  • (19条消息) 智能交互图谱丨AR全攻略

    AR全称Augmented Reality,即为增强现实,是指透过摄影机影像的位置及角度精算并加上图像分析技术,让屏幕上的虚拟世界能够与现实世界场景进行结合与交互的技术. 从2016年<Poké ...

  • (3条消息) AIoT时代的智能无线感知:特征、算法、数据集

    背景 人类对物理世界的感知经历了从主观感受到传感器再到传感网的发展阶段,如图1所示.在传感器变得越来越小.数据采集变得越来越普及的同时,有一个问题也日益突出--传感系统的部署成本太高.特别是随着感知范 ...