【C++】智能指针详解

news2024/11/17 1:37:43

一、从new和delete谈起

在C++中,可以使用new和delete关键字进行对象的创建和销毁,new一个对象实际上是在堆上分配内存,而new出来的对象也要自己用delete释放,从而回收内存,否则会造成内存的泄露。由程序员自己new来分配对象内存的方式成为动态分配。

其中有两个要点

  • new和delete需要成对使用,有new必然有delete,否则会导致内存泄漏,没有new分配的内存,不能使用delete来释放。
  • delete一块内存,只能delete一次,不可以delete多次,因为第一次delete之后,这块内存被回收会被分配给其他变量。可以在delete一个指针后将该指针设置为空ptr = nullptr。因为一个指针指向的内容即便被delete,该指针中依然保存着它所指向的那块动态内存地址,此时该指针被称为选空指针。如果此时将指针置空,则表示该指针不指向任何内存,这是一个良好的编程习惯

在使用动态分配时经常会有以下问题:

  • 内存泄漏:new了的对象再使用完后忘记delete导致内存一直被占用,相当于吃完饭不收盘子
  • 重复删除:对已经delete的对象进行再次delete,但是原内存空间被分配给了其他对象,导致回收到了其他的内存空间,相当于吃完饭把别人的盘子收了
  • 回收冲突:A和B均需要使用对象a,A使用完之后就顺手delete了a,但是B还需要使用a。相当于别人没吃完自己吃完,先把盘子收走了

总的来说,对内存进行管理依然是会让初学者头大的一个挑战,因此C++新标准中出现了智能指针。

二、智能指针

直接使用new一个对象的方式返回的是一个对象的指针,这个对象指针称为裸指针,这种指针功能强大使用灵活,但是需要全程负责维护,容易出错。在C++ 11中引入了新的指针——智能指针,智能指针对裸指针进行了包装,最突出的特性是智能指针能够自动释放所指向的对象。

C++标准库中规定了四种智能指针,分别是std::auto_ptr, std::unique_ptr, std:shared_ptr, std:weak_ptr,其中std::auto_ptr是C++ 98推出的,目前该智能指针已被std:unique_ptr取代,C++ 11中也明确弃用了该种智能指针。这三种指针其实都是类模板,可以将new获得的地址赋予给他们

  • shared_ptr是共享指针的概念,多个指针指向同一个对象,最后一个指针被销毁时,这个对象会被释放。
  • weak_ptr主要用于辅助shared_ptr工作
  • unique_ptr是一种独占式指针的概念,同一个时间内只能由一个指针指向该对象

2.1 shared_ptr

shared_ptr指针采用共享所有权来管理所指向对象的生存期,对象可以被多个shared_ptr指向,多个shared_ptr之间互相协作,从而确保只在不需要所指对象的时候才回收对象。shared_ptr的工作机制是引用计数,每一个shared_ptr指向相同的对象都会增加引用计数值,知道最后一个指向该对象的shared_ptr指针不再指向该对象的时候,才会析构对象

智能指针是一个类模板,使用尖括号规定指针指向的类型,使用形式如下

shared_ptr<指向的类型> 智能指针名

// 样例
shared_ptr<string> p1; //一个指向string的智能指针

当然,智能指针也可以进行带初值的初始化

shared_ptr<int> pi(new int(100));

这是使用裸指针初始化智能指针的场景,但是还会遇到一些陷阱,因此shared_ptr模板中内置了make_shared函数进行初始化,是最安全并且更高效的初始化方法,它能够在动态内存(堆)中分配并且初始化一个对象,然后返回指向此对象的shared_ptr,使用方法如下:

shared_ptr<int> p2 = std::make_shared<int>(10);
shared_ptr<string> p3 = std::make_shared<string>(10, 'hello');

2.1.2 shared_ptr常规操作

1.引用计数的增加
每个shared_ptr会记录有多少个其他shared_ptr指向同样的对象
(1) 使用智能指针对另一个智能指针进行初始化

auto p6 = std::make_shared<int>(100);
auto p7 = p6;

(2)把智能指针作为实参往函数中传递

void myfunc(shared_ptr<int> ptmp){
	return;
}

myfunc(p7);

这会增加指针引用数,当函数返回时减少
(3)作为函数返回值

shared_ptr<int> myfunc(shared_ptr<int> ptmp){
	return ptmp;
}

p8 = myfunc(p7);

2.引用计数减少
(1)shared_ptr指向新对象的时候,引用计数减少
(2)局部智能指针离开其作用域,比如上面(2)中智能指针作为实参传递进函数时增加引用数,函数执行完毕后减少
(3)当一个shared_ptr引用技术变为0的时候,会自动释放自己管理的对象

3.常规操作
(1)use_count成员函数:返回有多少个智能指针指向某个对象
(2)unique成员函数,是否仅有一个智能指针指向某对象
(3)reset成员函数,不带参数时,pi置空,原对象引用计数-1;带参数则将该智能指针指向参数中的对象

*4.解引用
返回智能指针p指向的对象

5.get成员函数
p.get()用于返回智能指针p中保存的指针,小心使用,若智能指针释放了所指向的对象,则返回的指针所指的对象也会失效。

2.2 weak_ptr简介

weak_ptr是一个智能指针,这种智能指针指向一个由shared_ptr管理的对象,但是这种指针并不控制所指向对象的生存期,也不会改变shaed_ptr的引用计数。使用样例如下:

auto pi = make_shared<int>(100);
weak_ptr<int> piw(pi);

既然weak_ptr所指向的对象有可能不存在,那么waek_ptr是不能直接用于访问对象的,必须要使用一个叫做lock的成员函数,lock的功能是检查weak_ptr所指向的对象是否还存在,如果是,lock能够返回一个指向共享对象的shared_ptr,如果不存在,则返回一个空的shared_ptr

常用操作
1.use_count成员函数
获取该弱指针共享对象的其他shared_ptr数量,或者说强引用计数的数量

2.expired
若该指针的use_count为0,则返回true,反则返回false

3.reset成员函数
将该弱指针设置为空,不影响该对象的强引用数量,但是指向该对象的弱引用数量减1

4.lock
获取一个弱指针所指向的对象的shared_ptr

2.2.1 shared_ptr和weak_ptr的大小

weak_ptr和shared_ptr的尺寸都是裸指针的2倍大小,在x86平台下,一个裸指针的sizeof时4Bytes,因此weak_ptr和shared_ptr的大小都是8Bytes。其实这8个字节包含了2个裸指针,如图示:
在这里插入图片描述
其中第一个裸指针指向智能指针所指向的对象,第二个指针指向一个控制块,这个控制块中有:1.所指对象的引用计数 2.所指对象的弱引用计数 3.其他数据,比如自定义的删除器指针

2.3 unique_ptr简介和常规操作

2.3.1 unique_ptr简介

unique_ptr智能指针是一种独占式智能指针,同一个时刻,只有一个unique_ptr指针指向这个对象,当这个unique_ptr被销毁的时候,它所指向的对象也会被销毁。

1.常规初始化(unique_ptr和new配合)

unique_ptr<int> pi;
unique_ptr<int> pi2(new int(105));

2.make_unique函数
C++ 11中没有make_unique函数,但是C++ 14提供了这个函数
和常规的初始化相比,要优先选择make_unique函数,这能有更好的性能。但是如果使用make_unqiue的话,就不能使用删除器,因为make_unique不支持指定删除器的语法

unique_ptr<int> p1 = std::make_unique<int>(100);
auto p2 = std::make_unique<int>(200);

2.3.2 unique_ptr常用操作

1.unique_ptr不支持的操作

unique_ptr<string> ps1(new string("I Love China!));
unique_ptr<string> ps2(ps1);	// 不支持复制
unique_ptr<string> ps3 = ps1;	// 不支持复制
unique_ptr<string> ps4;
ps4 = ps1;	// 不支持赋值动作

unique_ptr不允许复制、赋值等操作,是一个只能移动不能复制的语义

2.移动语义
可以通过std:move将一个unique_ptr转移到其他的unique_ptr中

unique_ptr<string> ps1(new string("I Love China!));
unique_ptr<string> ps3 = std::move(ps1);	// 转移后ps3为空了,ps3指向原来的ps1所指

3.release成员函数
放弃对指针的控制权,切断智能指针和其所指向对象之间的联系。返回指针,将智能指针置空,返回的裸指针可以手工delete食坊,也可以用于初始化另一个智能指针,或者给另外一个智能指针赋值。

4.reset成员函数
当reset不带参数的时候,释放智能指针指向对象,并且将智能指针置空,当reset带参数时,释放智能指针指向原来的内存,让该智能指针指向新内存。

5.=nullptr
释放智能指针所指向的对象,并且将智能指针置空

6.指向一个数组

std::unique_ptr<int[]> ptr_arr(new int[10]);
ptr_arr[0] = 12;

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

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

相关文章

[手机Linux PostmarketOS]五, docker安装和使用

docker容器 一&#xff0c;docker安装和配置 安装 docker 和 docker-compose&#xff1a; sudo apk add docker docker-cli-compose #安装docker sudo service docker start #启动docker服务 sudo rc-update add docker default #设置docker为自启动可选关…

【PostgreSQL003】PostgreSQL数据表空间膨胀,磁盘爆满,应用宕机(经验总结,已更新)

1.一直以来想写下基于PostgreSQL的系列文章&#xff0c;作为较火的数据ETL工具&#xff0c;也是日常项目开发中常用的一款工具&#xff0c;最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下PostgreSQL数据库相关知识体系。空间膨胀&#xff08;主键、外键、…

汇编语言:call、call far ptr、call word ptr、call dword ptr、call 寄存器

引言 call指令是转移指令&#xff0c;CPU执行call指令&#xff0c;进行两步操作&#xff1a; &#xff08;1&#xff09;将当前IP或当前CS和IP压入栈中 &#xff08;2&#xff09;转移。call指令不能短转移&#xff0c;除此之外&#xff0c;call指令转移的方法跟jmp指令的原理…

柔性超级电容器咋储能?生物聚合物在其中起啥作用?有啥挑战?

*本文只作阅读笔记分享* 一、引言 随着对化石燃料影响的日益关注&#xff0c;开发用于先进电化学能量存储设备的绿色和可再生材料变得至关重要。超级电容器因其出色的寿命、安全性和宽温度操作范围等优势而成为有前途的储能候选者。柔性超级电容器特别适合为轻质可穿戴电子设…

xss GAME (xss漏洞攻击1-8)

目录 xss网页链接 第一关 第二关 第三关 ​编辑第四关 ​编辑第五关 ​编辑第六关 第七关 第一种 Function构建函数 第二种 tostring parseInt 第三种 silce() ​编辑第八关&#xff08;安全过滤框架 dom破坏&#xff09; xss网页链接 XSS Game - Learning XSS Ma…

linux之网络子系统-GSO/TSO 源码分析

一、GSO/TSO GSO 目前在内核5.10.* 版本时&#xff0c;已经是合入主线&#xff0c;就是对TCP/UDP都支持并且在网络协议栈GSO功能是默认打开的。虽然可以通过ethtool -K 网卡名 gso off 关闭&#xff0c;但是在L3/L4还是走GSO逻辑&#xff0c;关不掉。我目前是没有找到内核源码…

NextJs - 服务端/客户端组件之架构多样性设计

NextJs - 服务端/客户端组件之架构多样性设计 前言一. 架构设计1.1 SSR流式渲染常见错误设计之 - 根页面同步阻塞1.2 架构设计之 - 客户端组件依赖于服务端组件数据① 使用 Redux 完成数据共享 1.3 架构设计之 - 单页内的分步骤跳转① 如何做到服务端组件和客户端组件之间的切换…

libevent之android与鸿蒙编译过程

背景 最近基于libevent开发了一个端侧的缓存代理库&#xff0c;先是基于macOS编译开发的&#xff0c;基本0问题&#xff0c;后来移植到鸿蒙与android时遇到一些编译链接问题。 libevent版本如下&#xff1a; 软件版本号libevent-2.1.8 android编译 编译环境 android studio…

EmguCV学习笔记

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 笔者的博客网址&#xff1a;https://blog.csdn.net/uruseibest 本教程将分为VB.Net和C#两个版本分别进行发布。 教程VB.net版本请…

Go Roadmap-Basics中文笔记

Go Roadmap-Basics 地址&#xff1a;https://roadmap.sh/golang 简介&#xff1a;Github star No.6 学习路线 Go 中译版 Learn the Basics Go特点&#xff1a;静态类型&#xff0c;运行速度快&#xff0c;编译语言&#xff0c;编译速度快&#xff0c;自动垃圾回收&#xff…

【2】初识JVM

目录 一.什么是JVM 二.JVM的功能 2.1即时编译 三.常见的JVM​编辑 ​编辑 总结​编辑 一.什么是JVM 二.JVM的功能 2.1即时编译 三.常见的JVM 总结

Spring MVC Controller返回json日期格式配置失效的解决办法

如题&#xff0c;Spring MVC 4.3.0版本&#xff0c;配置jackson读写json。Controller层方法返回值对象包含java.util.Date类型的属性&#xff0c;并且在applicationContext.xml中配置了jackson的日期格式&#xff1a; <mvc:annotation-driven><mvc:message-converters…

【ARM+Codesys 客户案例 】RK3568/A40i/STM32+CODESYS在工厂自动化中的应用:PCB板焊接机

现代化生产中&#xff0c;电子元件通常会使用自动化设备来进行生产&#xff0c;例如像PCB&#xff08;印刷电路板&#xff09;的组装。但是生产过程中也会面临一些问题&#xff0c;类似于如何解决在PCB板上牢固、精准地安装各种组件呢&#xff1f;IBL Lttechnik GmbH公司的CM80…

什么是OpenTiny?

OpenTiny 是一套企业级的 Web 前端开发解决方案&#xff0c;提供跨端、跨框架的 UI 组件库和低代码引擎&#xff0c;帮助开发者高效构建 Web 应用 。企业运用开发中&#xff0c;可以利用 OpenTiny 的以下核心组件和优势&#xff1a; TinyVue 组件库&#xff1a;一个丰富的组件库…

AWS boto3 脚本访问 AWS 资源

AWS boto3 脚本访问 AWS 资源 引言boto3主要功能常见用例安装和基本使用 boto3.Client() 低级客户端基本用法关键参数 boto3.resource() 高级客户端常见参数用法 boto3.resource VS boto3.client相似点不同点总结 关于身份验证凭证隐式身份凭证显式身份验证凭证assuem role如何…

对比各类 AWS MySQL 升级方案及原理

搞了好几个月的MySQL升级终于接近尾声&#xff0c;进入总结梳理阶段~ 本文主要对比升级期间用到的三种方案&#xff1a; 本地升级蓝绿升级API同步升级 对比项 \ 升级方式本地升级蓝绿升级API同步升级停机时间长&#xff0c;3-5分钟不可读写较短&#xff0c;约15秒实例变为只…

Delphi 实现JSON序列化和反序列化的功能以及源码探究

目录 一、JSON序列化和反序列化简介 二、Delphi序列化的两种方式 1、TJson的使用 2、TJsonSerializer的使用 3、使用注意事项 三、Delphi与GO序列化效率对比 1、GO语言JSON序列化方法 2、Delphi 与 GO 序列化效率对比 四、Delphi序列化源码初探 五、Delphi 序列化的优…

NGINX 之 location 匹配优先级

章节 1 NGINX 的源码安装 2 NGINX 核心配置详解 3 NGINX 之 location 匹配优先级 4 NGINX 基础参数与功能 目录 1 location 基础语法 1.1 location 语法说明表 1.2 URI部分简单介绍 2 location 匹配优先级 2.1 URI匹配的规则与顺序 2.2 精确匹配(location /1.txt) 2.3 区…

hutool发邮件功能如何配置SMTP服务器参数?

hutool发邮件的教程指南&#xff1f;hutool发邮件性能优化方法&#xff1f; Hutool作为一个轻量级的Java工具库&#xff0c;其邮件发送功能因其简单易用而受到广泛关注。AokSend将详细介绍如何通过配置SMTP服务器参数来实现Hutool发邮件的功能。 hutool发邮件&#xff1a;优势…

LinuxKernel开发

Linux Kernel简介 0. Linux历史 Linux内核&#xff08;英語&#xff1a;Linux kernel&#xff09;是一种开源的类Unix操作系统宏内核。整个Linux操作系统家族基于该内核部署在传统计算机平台&#xff08;如个人计算机和服务器&#xff0c;以Linux发行版的形式[7]&#xff09;…