C++ 智能指针shared_ptr

文章目录

  • C++智能指针shared_ptr概述
  • shared_ptr使用方法
  • shared_ptr引用计数的增加与减少
  • shared_ptr常用操作

C++智能指针shared_ptr概述

在介绍智能指针前先看如下代码

void func1(std::string& str)
{std::string* ps = new std::string(str);str = *ps;return;
}
void func2(std::string& str)
{std::string* ps = new std::string(str);if (weird_thing())throw ecception();str = *ps;delete ps;return;
}

对于func1函数, 由于我们忘记了delete,该函数的每一次的调用,都会在堆区中开辟内存,但从不收回,从而导致内存泄漏.对于func2我们没有忘记delete,但当程序出现异常的时候,会从if语句处结束函数调用,下面的delete并没有执行,从而导致内存泄漏.程序总会在我们粗心大意的时候遇到一些内存的问题.因为ps指针是一个常规的指针,所以我们很难对其管理,如果他是一个对象,那么在对象过期的时候他就会自动销毁.所以这引入了智能指针.

智能指针概述:
C++中引入智能指针(smart pointer)的主要目的是为了管理动态内存块,以确保内存块的安全和高效。智能指针是一种特殊的指针类型,它可以在内存块被释放之前自动释放内存块,从而避免了手动释放内存块的繁琐和耗时。

shared_ptr使用方法

  • shared_ptr介绍:

C++中的std::shared_ptr是一种智能指针类型,用于管理动态内存块。如果要创建智能指针对象,需要包含头文件memory,c++中智能指针有四种分别是auto_ptr,shared_ptr,unique_ptr,weak_ptr这里首先介绍shared_ptr.

  • shared_ptr定义方式
shared_ptr<类型> 指针名字;
  • 案例
	shared_ptr<double> pd; // 定义了一个空智能指针shared_ptr<int> pi(new int(100)); // pi指向一个4字节大小的地址块,存放值为100;double* p_reg = new double;// 对于智能指针都有一个explicit构造函数, 可以将常规指针作为参数传递给智能指针, 但不推荐这么做,可能会引发问题.pd = shared_ptr<double>(p_reg); // 允许强制转换shared_ptr<double> pd_1 = p_reg; // 不能将常规指针隐式转换为智能指针, 只允许强制转换shared_ptr<double> pd_2(p_reg); // 可以将常规指针作为参数传递给智能指针

shared_ptr引用计数的增加与减少

  1. shared_ptr的引用计数
    对于shared_ptr它会记录有多少个其他的shared_ptr指向相同的对象,当指向它的对象过期时, 如果只有一个指向它的指针,则会将指针指向的内存销毁,如果不是最后一个,他会将过期的指针对象置空
	shared_ptr<double> pd1 = make_shared<double>(100.34); // 在调试过程中,将鼠标悬停到pd1上会出现1 strong, 代表一个强引用,auto pd2 = pd1; // 同上所述, 当悬停时会出现2 strong代表2个强引用.

std::make_shared函数可以将一个对象包装成智能指针.它返回一个指向该对象的std::shared_ptr对象.

  1. 引用计数的增加
    如下情况会增加shared_ptr的引用计数
    a)像上面一样用一个智能指针对象初始化另外一个智能指针对象
    b)把智能指针用传值的方式进行函数参数的传递
    c)作为函数的返回值

  2. 引用计数的减少
    如下情况会减少shared_ptr的引用计数减少
    a)给shared_ptr赋予新值,指向别的内存,会导致shared_ptr引用计数减少
    b)局部的shared_ptr离开其作用域
    c)当一个shared_ptr从1变成零,它会释放其指向的对象

	shared_ptr<double> pd1 = make_shared<double>(100.34);shared_ptr<double> pd2 = pd1;shared_ptr<double> pd3(new double(300.3));pd1 = pd3;// pd1的指向改变, 指向的引用对象的计数会增加, 但原先的对象由于计数从1变成零所以原来的对象会被销毁.

shared_ptr常用操作

  1. use_count(): 返回某个对象的智能指针数
	shared_ptr<int> a(new int);a.use_count(); // 返回1;shared_ptr<int> b(a);b.use_count(); a.use_count(); // 返回2;
  1. unique(): 某对象的智能指针数为1返回true否则返回false
	shared_ptr<int> a(new int);a.unique(); // 返回true;shared_ptr<int> b(a);a.unique(); // (b.unique()) 返回false;
  1. reset():
    a)reset参数为空
    若指针是唯一指向该对象的, 则释放指针指向的对象,将指针置空,若指针不是唯一的,不会释放指向的对象, 但会将指针置空, 计数减一
	shared_ptr<int> p1(new int(400));auto p2(p1);p1.reset(); // 将p1置空, 引用计数减一p2.reset(); // 释放指向的对象, 将指针置空

b)reset带参数时(一般都是new 出来一个新的指针)
若指针指向的对象唯一, 释放指针指向的对象, 指向新对象.若指针指向的对象不唯一,则不会释放指向的对象, 但会将指针指向为新的对象,计数减一

	shared_ptr<int> p1(new int(400));auto p2(p1);p1.reset(new int); // 不会释放指向的对象, 指向新的对象p2.reset(new int); // 释放指向的对象, 指向新的对象
  1. 解引用: 获得指向的对象
shared_ptr<int> a(new int(100));
cout << *a << endl; // 打印100;
  1. get(): 返回指针中保存的指针(裸指针)
    裸指针是指,用new直接开辟的指针(如p = new int, p1 = new int(200))
	shared_ptr<int> p(new int(100));int* pi = p.get();cout << *pi << endl;注意: 不能释放pi(delete pi), 否则会导致不可预料的后果
  1. swap(): 交换两个智能指针指向的对象
	shared_ptr <string> p1(new string("C++"));shared_ptr<string> p2(new string("Java"));swap(p1, p2); or p1.swap(p2);
  1. =nulllptr :将所指向的对象, 引用计数减一, 若引用计数为零, 释放指向的对象.
	shared_ptr<int> p(new int(100));p = nullptr;
  1. 智能指针判断: 可以将智能指针作为判断条件
	shared_ptr<int> p(new int(100));p = nullptr;if (p) cout << "p指向一个对象" << endl;else cout << "没有指向对象" << endl;
  1. 指定删除器以及数组问题
    a)智能指针里面保存了一个裸指针, 在对象过期时会自动析构(析构方式为delete p(保存的裸指针)), 我们可以自己提供删除器,当我们指定了自己的删除器, 编译器会自动选择我们的删除器.将删除器作为参数名,即可调用自己的删除器
void My_Del(int* p)
{cout << "哈哈" << endl;delete p;
}
shared_ptr<int> p(new int(100),My_Del);
p.reset(); // 释放对象, 打印哈哈

b) 删除器也可以是一个lambda表达式

shared_ptr<int> p(new int(100), [](int* p) { delete p; });

c)删除器不能删除动态数组(如new int[10]), 原因是,默认的删除器删除的是指针所指向的裸指针(delete p) 而不是(delete [] p), 所以在这种情况下我们必须要提供自己的删除器

shared_ptr<int> p(new int[100], [](int* p) { delete []p; });

除了定义函数和lambda表达式指定为删除器,也可以使用default_delete来做删除器, default_delete是标准库里面的模板类

shared_ptr<Type> p(new Type[100, default_delete<Type[]>());

在定义数组的时候可以直接添加[]就可以实现删除器

shared_ptr<int[]> p(new int[100]);

下面介绍一种封装智能指针数组的例子,以后会经常遇见

template<typename T>
shared_ptr<T> make_shared_arry(size_t size)
{return shared_ptr<T>(new T[size], std::default_delete<T[]>());
}shared_ptr<int> pArr = make_shared_arry<int>(5); // 返回一个数组

d)指定删除器的额外说明
就算是两个shared_ptr指定了不同的删除器, 只要他们所指向的对象类型相同, 那么这两个shared_ptr也属于同一个类型.

	auto lambda1 = [](int* p) { delete p; };auto lambda2 = [](int* p) { delete p; };shared_ptr<int> p1(new int(100), lambda1); // 指定删除器为1shared_ptr<int> p2(new int(200), lambda2); // 指定删除器为2;p2 = p1; // p2会先调用2, 释放指向的对象, 然后指向p1所指向的对像, p1计数增加为2//当main()执行完毕后, 会调用1来释放,p1, p2, 虽然p2的删除器为2, 所以他们是同一类型, 所以调用了同一删除器.vector<shared_ptr<int> num(p1, p2) // 类型相同, 可以放到同一个容器

补充: 对于make_shared能够生成shared_ptr, 但是使用了make_shared之后就不能指定自己的删除器

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

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

相关文章

ELK企业级日志分析系统

目录 一、ELK 概述 1.ElasticSearch 2.Kiabana 3.Logstash 可以添加的其它组件 1.Filebeat 2.Fluentd 三、为什么要使用 ELK 四、ELK 的工作原理 五、 ELK Elasticsearch 集群部署 更改主机名、配置域名解析、查看Java环境 部署 Elasticsearch 软件 修改elasticsearc…

自然语言处理从入门到应用——LangChain:提示(Prompts)-[示例选择器(Example Selectors)]

分类目录&#xff1a;《自然语言处理从入门到应用》总目录 如果我们拥有大量的示例&#xff0c;我们可能需要选择在提示中包含哪些示例。ExampleSelector是负责执行此操作的类。 其基本接口定义如下所示&#xff1a; class BaseExampleSelector(ABC):"""Interf…

爬虫获取电影数据----以沈腾参演电影为例

数据可视化&分析实战 1.1 沈腾参演电影数据获取 文章目录 数据可视化&分析实战前言1. 网页分析2. 构建数据获取函数2.1 网页数据获取函数2.2 网页照片获取函数 3. 获取参演影视作品基本数据4. 电影详细数据获取4.1 导演、演员、描述、类型、投票人数、评分信息、电影海…

Wisej.NET Crack,Wisej.NET的核心功能

Wisej.NET Crack&#xff0c;Wisej.NET的核心功能 Wisej.NET是一个跨平台的web框架&#xff0c;用于使用.NET和C#/VB.NET而不是HTML和JavaScript构建现代HTML5应用程序。它包含创建任务关键型web应用程序所需的一切&#xff0c;包括UI组件、会话处理、状态管理和后端集成。借助…

单元测试之 - Spring框架提供的单元/集成测试注解

Spring框架提供了很多注解来辅助完成单元测试和集成测试(备注&#xff1a;这里的集成测试指容器内部的集成测试&#xff0c;非系统间的集成测试)&#xff0c;先看看Spring框架提供了哪些注解以及对应的作用。RunWith(SpringRunner.class) / ExtendWith(SpringExtension.class)&…

设计模式行为型——备忘录模式

目录 什么是备忘录模式 备忘录模式的实现 备忘录模式角色 备忘录模式类图 备忘录模式举例 备忘录模式代码实现 备忘录模式的特点 优点 缺点 使用场景 注意事项 实际应用 什么是备忘录模式 备忘录模式&#xff08;Memento Pattern&#xff09;又叫做快照模式&#x…

高并发负载均衡---LVS

目录 前言 一&#xff1a;负载均衡概述 二&#xff1a;为啥负载均衡服务器这么快呢&#xff1f; ​编辑 2.1 七层应用程序慢的原因 2.2 四层负载均衡器LVS快的原因 三&#xff1a;LVS负载均衡器的三种模式 3.1 NAT模式 3.1.1 什么是NAT模式 3.1.2 NAT模式实现LVS的缺点…

openwr折腾记7-Frpc使用自主域名解析透传本地服务免费不断线的探索

Frpc使用自主域名解析透传本地服务 综述frp透传http服务结构流程 第一部分openwrt-frpc客户端配置和使用指定服务器指定规则在自己的域名运营商处添加域名解析 第二部分shell编码实现frp自由切换服务器并更新dns解析获取切换服务器参数脚本实现切换脚本更新DNS解析打开openwrt计…

MySQL — InnoDB事务

文章目录 事务定义事务特性事务隔离级别READ UNCOMMITTEDREPEATABLE READREAD COMMITTEDSERIALIZABLE 事务存在的问题脏读&#xff08;Dirty Read&#xff09;不可重复读&#xff08;Non-repeatable Read&#xff09;幻读&#xff08;Phantom Read&#xff09; 事务定义 数据库…

Android Handler 的基本使用

1.前言 https://developer.android.google.cn/reference/android/os/Handler.html Handler 是 Android 中线程通信的常用方式&#xff0c;文档如是说&#xff1a; A Handler allows you to send and process Message and Runnable objects associated with a threads Message…

(十三)大数据实战——hadoop集群之YARN高可用实现自动故障转移

前言 本节内容是关于hadoop集群下yarn服务的高可用搭建&#xff0c;以及其发生故障转移的处理&#xff0c;同样需要依赖zookeeper集群的实现&#xff0c;实现该集群搭建时&#xff0c;我们要预先保证zookeeper集群是启动状态。yarn的高可用同样依赖zookeeper的临时节点及监控&…

构建器/建造者/构建者模式(C++)

定义 将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)。 应用场景 在软件系统中&#xff0c;有时候面临着“一个复杂对象”的创建工作&#xff0c;其通常由各个部分的子对象用一定的算法构成;由于需求的变化&#xff0c;这个复杂对象…

淘宝API开发(一)简单介绍淘宝API功能接口作用

前一阵子按照上级指示&#xff0c;根据淘宝API开发符合自已应用的系统&#xff0c;比如批量上传&#xff0c;批量修改名称&#xff0c;价格等功能什么的&#xff0c;在此就将我的开发历程写一写&#xff0c;为自己前段时间的工作做个总结。 淘宝开发平台(淘宝网 - 淘&#xff…

Android应用开发(6)TextView进阶用法

Android应用开发学习笔记——目录索引 上一章Android应用开发&#xff08;5&#xff09;文本视图&#xff08;TextView&#xff09;介绍了文本视图&#xff08;TextView&#xff09;设置文本内容、设置文本大小、设置文本显示颜色。 TextView是最基础的文本显示控件&#xff…

PHP正则绕过解析

正则绕过 正则表达式PHP正则回溯PHP中的NULL和false回溯案例案例1案例2 正则表达式 在正则中有许多特殊的字符&#xff0c;不能直接使用&#xff0c;需要使用转义符\。如&#xff1a;$,(,),*,,.,?,[,,^,{。 这里大家会有疑问&#xff1a;为啥小括号(),这个就需要两个来转义&a…

Linux 下设置开机自启动的方法

文章目录 事先准备对于普通的 Linux对于 RedHat Enterprise Linux 9 笔者的运行环境&#xff1a; 设置成功过的 Linux&#xff1a; RedHat Enterprise Linux 9 x86_64 CentOS 8 x86_64 事先准备 进行这个教程之前&#xff0c;必须要先安装好一个 Linux 操作系统。这个 Linux…

JavaWeb 手写Tomcat底层机制

目录 一、Tomcat底层整体架构 1.简介 : 2.分析图 : 3.基于Socket开发服务端的流程 : 4.打通服务器端和客户端的数据通道 : 二、多线程模型的实现 1.思路分析 : 2.处理HTTP请求 : 3.自定义Tomcat : 三、自定义Servlet规范 1. HTTP请求和响应 : 1 CyanServletRequest …

《面试1v1》ElasticSearch基础

&#x1f345; 作者简介&#xff1a;王哥&#xff0c;CSDN2022博客总榜Top100&#x1f3c6;、博客专家&#x1f4aa; &#x1f345; 技术交流&#xff1a;定期更新Java硬核干货&#xff0c;不定期送书活动 &#x1f345; 王哥多年工作总结&#xff1a;Java学习路线总结&#xf…

工厂方法模式

工厂模式&#xff08;Factory Pattern&#xff09;是一种创建型设计模式&#xff0c;它提供了一种统一的接口来创建对象&#xff0c;但将对象的实例化延迟到子类中。工厂模式主要解决了对象的创建过程与使用客户端代码的解耦&#xff0c;使得客户端代码不需要知道具体的对象创建…

Kafka的配置和使用

目录 1.服务器用docker安装kafka 2.springboot集成kafka实现生产者和消费者 1.服务器用docker安装kafka ①、安装docker&#xff08;docker类似于linux的软件商店&#xff0c;下载所有应用都能从docker去下载&#xff09; a、自动安装 curl -fsSL https://get.docker.com | b…