【C++11智能指针】unique_ptr

news2024/9/21 22:45:50

【C++11智能指针】unique_ptr

概述

一个 unique_ptr “拥有”它所指向的对象。
与 shared_ptr 不同,某个时刻只能有一个 unique_ptr 指向一个给定对象。
当 unique_ptr 被销毁时,它所指向的对象也被销毁

初始化

直接初始化

unique_ptr<int> p1;               // 可指向int对象的一个空智能指针p1

unique_ptr<int> p2(new int(100)); // 指向int对象的智能指针p2,int对象的值为100

auto p3(new int(100));            // 不能简写为auto,这里auto推断出p3是普通指针

make_unique函数

unique_ptr<int> p1 = make_unique<int>(100);  // 指向int对象的智能指针p2,int对象的值为100

auto p2 = make_unique<int>(200);             // 指向int对象的智能指针p2,int对象的值为100

std::make_shared是C++11的一部分,可惜的是,std::make_unique不是,它在C++14才纳入标准库。

make_unique 不支持指定删除器。如果不用删除器,建议优先使用 make_unique,拥有更高的性能。

拷贝构造与拷贝赋值

由于一个 unique_ptr 拥有它指向的对象,因此 unique_ptr 不支持普通的拷贝构造和拷贝赋值操作:

#include <iostream>
#include <memory>
using namespace std;

int main()
{
	unique_ptr<string> p1(new string("hello"));

	unique_ptr<string> p2(p1);  // 错误:unique_ptr不支持拷贝构造

	unique_ptr<string> p3 = p1; // 错误:unique_ptr不支持拷贝赋值

	return 0;
}

移动构造与移动赋值

#include <iostream>
#include <memory>
using namespace std;

int main()
{
	unique_ptr<string> p1(new string("hello"));

	unique_ptr<string> p2(std::move(p1));  // 移动构造一个新的智能指针p2,
										   // p1变成空,p2指向该对象

	unique_ptr<string> p3 = std::move(p1); // 移动赋值,
										   // p1变成空,p3指向该对象
	return 0;
}

常用操作

在这里插入图片描述
在这里插入图片描述

release()

p.release() 返回裸指针,同时将该智能指针 p 置空。也就是说,调用 release() 会切断 unique_ptr 和它原来管理的对象间的联系。

release() 返回的指针通常被用来初始化另一个智能指针或给另一个智能指针赋值。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
	unique_ptr<string> p1(new string("hello"));

	unique_ptr<string> p2(p1.release());  // release()返回p1当前指向对象的裸指针,并将p1置为空。
										  // p2通过返回的裸指针初始化,唯一拥有该对象,即将对象所有权从p1转移给p2。
	return 0;
}

在上面代码中,管理内存的责任简单地从一个智能指针p1转移给另一个智能指针p2。但是,如果我们不用另一个智能指针来保存release() 返回的指针,仅仅切断了p1与对象的内存管理责任,我们的程序就要负责内存的释放:

#include <iostream>
#include <memory>
using namespace std;

int main()
{
	unique_ptr<string> p1(new string("hello"));
	
	// p1.release();          // 错误!!程序没有进行内存的释放,导致内存泄漏

	string* p = p1.release(); // 可以简写为auto p = p1.release();

	delete p;                 // 手动delete释放

	return 0;
}

虽然我们不能拷贝或赋值 unique_ptr,但可以通过调用 release() 或 reset() 将指针的所有权从一个(非const)unique_ptr 转移给另一个 unique_ptr。

get()

p.get():返回智能指针p中保存的裸指针。因为有些第三方库的函数参数需要的是内置裸指针,所以引入该函数。

注意:release()也返回指针p中保存的裸指针,并且切断了p与原指向对象的联系,p置为空,但原对象依然存在;get()只返回p中保存的裸指针,并未切断p与指向对象的联系,仍“拥有”原对象。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
	unique_ptr<string> up(new string("hello"));

	string* p = up.get();
	*p = "world";

	//delete p;  // 这里不要delete,up仍拥有原对象
				 // 否则会产生不可预料的后果
	return 0;
}

reset()

无参数:reset()

若 reset 不带参数:释放智能指针所指向的对象,并将智能指针置空。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
	unique_ptr<string> p1(new string("hello"));
	
	p1.reset();  // 释放指向的对象,指针p1置为空

	return 0;
}

有参数reset(ptr)

若 reset 带参数:释放智能指针所指向的对象,并让该智能指针指向新对象。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
	unique_ptr<string> p1(new string("hello"));

	p1.reset(new string("world"));  // p1释放原指向对象的内存,指向新对象

	unique_ptr<string> p2(new string("world"));

	p1.reset(p2.release());         // release()返回p2当前指向对象的裸指针,并将p2置为空
								    // reset()释放p1原指向对象的内存,指向裸指针指向的对象

	return 0;
}

= nullptr

p = nullptr:释放智能指针所指向的对象,并将智能指针置空。
**注意:**与无参数reset()作用相同。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
	unique_ptr<string> p(new string("hello"));

	p = nullptr;  // 释放p所指向的对象,并将p置空

	return 0;
}

swap()

交换两个智能指针所指向的对象。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
	unique_ptr<string> p1(new string("hello"));
	unique_ptr<string> p2(new string("world"));
	p1.swap(p2);  // 等价于std::swap(p1, p2);

	return 0;
}

指向一个数组

#include <iostream>
#include <memory>
using namespace std;

class A
{
public:
	A() { cout << "A()" << endl; }
	~A() { cout << "~A()" << endl; }
};

int main()
{
	unique_ptr<A[]> p(new A[10]);  		// 如果有自己的析构函数,那么定义时<>里面必须加上[]
								   		// 否则会报异常
	unique_ptr<int[]> p(new int[10]);
	p[0] = 110;
	p[1] = 120;

	return 0;
}

解引用

*p 解引用:获取该智能指针指向的对象,可以直接操作。
如果定义的内容是数组,没有解引用运算符。

#include <iostream>
#include <memory>
using namespace std;

int main()
{
	unique_ptr<string> p1(new string("hello"));
	*p1 = "world";

	unique_ptr<int> p2(new int(100));
	*p2 = 200;
	
	unique_ptr<int[]> p(new int[10]);  // 如果定义的内容是数组,没有解引用运算符
	
	return 0;
}

转换为shared_ptr

如果 unique_ptr 为右值,就可以将它赋值给 shared_ptr。
因为 shared_ptr 包含一个显式构造函数,可用于将右值 unqiue_ptr 转换为 shared_ptr,shared_ptr 将接管原来归 unique_ptr 所拥有的对象。

#include <iostream>
#include <memory>
using namespace std;

auto myfunc()
{
	return unique_ptr<string>(new string("hello"));  // 临时对象都是右值
}

int main()
{
	shared_ptr<string> sp = myfunc();                // 这里系统会为shared_ptr创建控制块

	unique_ptr<string> up(new string("world"));
	shared_ptr<string> sp = std::move(up);
	return 0;
}

返回unique_ptr

不能拷贝 unique_ptr 的规则有一个例外:我们可以拷贝或赋值一个将要被销毁的 unique_ptr。最常见的例子是从函数返回一个 unique_ptr。

如果没有智能指针去接返回的unique_ptr,临时对象会被释放,同时会释放掉所指向的对象的内存.

#include <iostream>
#include <memory>
using namespace std;

unique_ptr<int> fun(int value)
{
	unique_ptr<int> p(new int(value));
	return p;  						// 返回局部对象,系统会生成一个临时unique_ptr对象
									// 然后调用unique_ptr的移动构造函数

}

int main()
{
	unique_ptr<int> up = fun(120);  // 如果用up来接,则临时对象直接构造在up里。
	 								// 如果不接,则临时对象会被释放,同时会释放掉所指向的对象的内存。

	return 0;
}

删除器

unique_ptr 指定删除器,先要在类型模板参数中传递进去类型名,然后在构造参数中再给具体的删除器函数名

#include <iostream>
#include <memory>
using namespace std;

void mydeleter(string* pdel)
{
	delete pdel;
	pdel = nullptr;
}

int main()
{
	typedef void(*fp)(string*);  // 定义一个函数指针类型,类型名为fp

	unique_ptr<string, fp> up(new string("hello"), mydeleter);

	return 0;
}

即使两个 shared_ptr 指定的删除器不相同,但只要他们所指向的对象相同,那么这两个 shared_ptr 的类型相同,即可以放到同一个容器里。

但是 unique_ptr 不一样,如果两个 unique_ptr 指定的删除器不同,则相当于 这两个unique_ptr 的类型不同,即不能放到同一个容器里。

unique_ptr的大小

通常情况下,unique_ptr 的大小跟裸指针一样。

如果增加了自己的删除器,则 unique_ptr 的尺寸可能增加,也可能不增加。如果 lambda 表达式作为删除器,尺寸就没变化。如果定义一个函数作为删除器,尺寸发生变化。

对比shared_ptr:shared_ptr不管指定什么删除器,shared_ptr 的尺寸都是裸指针的 2 倍。

参考资料

【C++11智能指针】unique_ptr概述、初始化、常用操作、返回unique_ptr、指定删除器、尺寸
C++智能指针shared_ptr、unique_ptr以及weak_ptr

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/341879.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AcWing 166. 数独(DFS + 剪枝优化 + lowbit函数 + 状态压缩)

AcWing 166. 数独&#xff08;DFS 剪枝优化 lowbit函数 状态压缩&#xff09;一、题目二、分析1、状态压缩2、lowbit函数&#xff08;1&#xff09;函数作用&#xff08;2&#xff09;函数实现3、DFS思路4、剪枝优化三、代码一、题目 二、分析 1、状态压缩 那么如果针对某一…

情人节专场即将到来,各大平台各显神通

一年一度的情人节即将到来&#xff0c;情人节除了有情侣、恋人之间表达爱意或者追求浪漫之外&#xff0c;也有很多人想要购买一些特殊的礼物送给自己的爱人或者亲人。对于跨境电商而言&#xff0c;这个时段往往能更好的将品牌与社交媒体上的目标客户建立起联系&#xff0c;同时…

【青训营】分布式定时任务简述

这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天 分布式定时任务简述 定义 定时任务是指系统为了自动完成特定任务&#xff0c;实时、延时、周期性完成任务调度的过程。分布式定时任务是把分散的、可靠性差的定时任务纳入统一平台&#xff0c;并且实现集群管理调度和…

OpenCV-PyQT项目实战(6)项目案例02滚动条应用

欢迎关注『OpenCV-PyQT项目实战 Youcans』系列&#xff0c;持续更新中 OpenCV-PyQT项目实战&#xff08;1&#xff09;安装与环境配置 OpenCV-PyQT项目实战&#xff08;2&#xff09;QtDesigner 和 PyUIC 快速入门 OpenCV-PyQT项目实战&#xff08;3&#xff09;信号与槽机制 …

JumpServer开源堡垒机v3.0版本设计重点解读

编者注&#xff1a;在1月17日的JumpServer开源堡垒机v3.0预发布恳谈会直播中&#xff0c;JumpServer创始人广宏伟与大家分享了JumpServer v3.0版本的设计思路与功能亮点。在v3.0版本正式发布之前&#xff0c;JumpServer开源项目组基于此次直播内容为大家整理总结了JumpServer v…

C++11可变模板参数

C11可变模板参数一、简介二、语法三、可变模版参数函数3.1、递归函数方式展开参数包3.2、逗号表达式展开参数包一、简介 C11的新特性–可变模版参数&#xff08;variadic templates&#xff09;是C11新增的最强大的特性之一&#xff0c;它对参数进行了高度泛化&#xff0c;它能…

STM32单片机红外遥控

红外遥控接口电路STM32单片机红外遥控程序源代码#include "sys.h"#define LED_RED PBout(12) //红色发光二极管控制管脚初始化PB12#define LED_GREEN PBout(13) //绿色发光二极管控制管脚初始化PB13#define LED_YELLOW PBout(14) //黄色发光二极管控制管脚初始化PB14…

反射,枚举,lambda表达式

目录 1、反射 1.1 基本概念 1.2 反射相关的类 1.3 创建 Class 对象 1.4 反射的使用 1.4.1 通过反射创建对象&#xff1a; 1.4.2 获取私有的构造方法 1.4.3 获取私有的成员变量 1.4.4 获取私有的方法 1.5 总结 2、枚举 2.1 认识枚举 2.2 使用枚举 2.3 枚举与反射…

第4章 流程控制-if-else,Switch,For循环(循环守卫,循环步长,倒叙打印),While循环,多重循环...

第 4 章 流程控制-if-else,Switch,For循环(循环守卫&#xff0c;循环步长&#xff0c;倒叙打印)&#xff0c;While循环&#xff0c;多重循环 4.1 分支控制 if-else 让程序有选择的的执行&#xff0c;分支控制有三种&#xff1a;单分支、双分支、多分支 4.1.1 单分支 1)基本语法…

Leetcode-每日一题1234. 替换子串得到平衡字符串(滑动窗口 + 哈希表)

题目链接&#xff1a;https://leetcode.cn/problems/replace-the-substring-for-balanced-string/description/ 思路 题目意思 这题意思是一个只含有[Q, W, E, R] 四个字符的字符串s且长度一定是 4的倍数&#xff0c; 需要你通过替换子串&#xff0c;使他变成一个「平衡字符…

【C++设计模式】学习笔记(6):Bridge 桥模式

目录 简介动机(Motivation)模式定义结构(Structure)要点总结笔记结语简介 Hello! 非常感谢您阅读海轰的文章,倘若文中有错误的地方,欢迎您指出~ ଘ(੭ˊᵕˋ)੭ 昵称:海轰 标签:程序猿|C++选手|学生 简介:因C语言结识编程,随后转入计算机专业,获得过国家奖学金…

【C++设计模式】学习笔记(2):模式分类与模版方法 Template Method

目录 简介模式分类GOF-23 模式分类从封装变化角度对模式分类重构获得模式 Refactoring to Patterns重构关键技法“组件协作”模式Template Method 模式动机(Motivation)结构化软件设计流程面向对象软件设计流程早绑定与晚绑定模式的定义结构(Structure)要点总结结语简介 He…

Unity(一)--通过简单例子了解属性、脚本等基础操作

目录新建工程保存视图创建游戏对象调整场景视图角度添加物理运动组件更换材质更改颜色添加脚本点击向上跳跃变色旋转UGUI的使用修改文字内容新建工程 ps:最好不使用中文文件路径及名称&#xff0c;可能会报错。 保存视图 进工程后&#xff0c;此时只有空文件夹&#xff0c;可…

《痞子衡嵌入式半月刊》 第 71 期

痞子衡嵌入式半月刊&#xff1a; 第 71 期 这里分享嵌入式领域有用有趣的项目/工具以及一些热点新闻&#xff0c;农历年分二十四节气&#xff0c;希望在每个交节之日准时发布一期。 本期刊是开源项目(GitHub: JayHeng/pzh-mcu-bi-weekly)&#xff0c;欢迎提交 issue&#xff0c…

新手做跨境电商,选对平台很重要

据数据统计&#xff0c;我国跨境电商早在2013年&#xff0c;发展态势就十分迅猛&#xff0c;交易规模达到2.7万亿元&#xff1b;而到2015年&#xff0c;仅仅两年时间&#xff0c;交易规模就突破到5万亿元&#xff1b;再过两年&#xff0c;2017年跨境电商交易规模增长到7.6万亿元…

okcc呼叫中心怎样搭建?

随着企业对服务逐渐重视&#xff0c;开始意识到呼叫中心作为客户服务水平和沟通效率的基本平台&#xff0c;并且有越来越多的企业开始利用呼叫中心外呼方式销售自己的产品。在此背景下&#xff0c;很多企业&#xff0c;无论规模大小&#xff0c;都有建立自己呼叫中心的想法。 …

docker的资源控制管理——Cgroups

引言&#xff1a;docker 使用cgrqup控制资源&#xff0c;K8S 里面也有limit&#xff08;使用上限&#xff09;docker通过cgroup来控制容器使用的资源配额&#xff0c;包括CPU、内存、磁盘三大方面&#xff0c;基本覆盖了常见的资源配额和使用量控制。Cgroup 是 Control group 的…

Jboss EAP 7.4.8配置jacoco 端口无法开启 启动报错

项目场景&#xff1a; servers: Jboss EAP 7.4.8 JDK: JDK17 jacoco: 测试jacoco-0.7.9 jacoco-0.8.5 jacoco-0.8.8 问题描述 问题1: Jboss 启动 VM arguments追加, 如下启动参数&#xff1a; -javaagent:F:\CoverageReport\jacoco-0.7.9\lib\jacocoagent.jarincludes*,o…

别具一格,原创唯美浪漫情人节表白专辑,(复制就可用)(html5,css3,svg)表白爱心代码(1)

别具一格&#xff0c;原创唯美浪漫情人节表白专辑&#xff0c; (复制就可用)&#xff08;html5,css3,svg)表白爱心代码(1) 一、 前言 回眸之间&#xff0c;丰盈了岁月&#xff0c;涟漪了思绪&#xff0c;轻轻落笔&#xff0c;不写伤痕&#xff0c;不写仇怨&#xff0c;只写岁月…

反光板导航SLAM(三)反光柱导航开发与实验

在上一章中简单了解了VEnus算法对于反光柱导航的基本思路。其主要分为了高反点提取、高反点聚类查找中心、高反点与已知反光柱位姿匹配以及调用ceres库进行位姿优化等步骤。然后在这个算法的基础上&#xff0c;再进行一定的开发达到一个比较稳定且可视化的版本。 使用&#xff…