C++11 谈谈shared_ptr

2023-11-10

C++11 谈谈shared_ptr(细节)

个人用!十分主观!仅供参考!

shared_ptr是C++11中加入的一种智能指针(其实并不够智能),其作用就是帮助我们管理在堆中开辟的空间,避免野指针等众多内存管理不当造成的问题。

重点:智能指针会自动的给我们释放开辟的内存空间

实际上,每种智能指针都是以类模板的方式实现的,shared_ptr

shared_ptr使用了引用计数机制,也就是其类内维护了一个计数count。其之所以叫做shared,多个shared_ptr可以共同使用同一块堆内存,一旦多了一个新的shared_ptr使用了这块内存,这个计数count就会+1。

初始化

//显式
shared_ptr<int> p1;//默认初始化为nullptr,默认构造
shared_ptr<int> p2(new int(10));//使用指针初始化,含参构造
shared_ptr<int> p3(p2);//拷贝构造
shared_ptr<int> p4(move(p3));//移动构造

不够智能的智能指针

注意,不能用同一个普通指针初始化超过一个智能指针。

int* p = new int(10);
shared_ptr<int> p1(p);
shared_ptr<int> p2(p);

上面的语句编译不会出错,但运行时会崩掉,因为程序结束时,智能指针会自动释放p1,p2的内存。在此p1,p2的计数都是1,故p这块内存被释放了两次,释放第二次的时候程序崩掉了。

同理,还有下面这种情况:

int* p = new int(10);
shared_ptr<int> p1(p);
delete p;

同样的原因,释放已经释放的内存,程序崩掉了。

因此,智能指针的使用要和传统的指针用法相区别开。

shared_ptr模板类提供的成员方法

参考文章C++11 shared_ptr智能指针(超级详细) (biancheng.net)

成员方法名 功 能
operator=() 重载赋值号,使得同一类型的 shared_ptr 智能指针可以相互赋值。
operator*() 重载 * 号,获取当前 shared_ptr 智能指针对象指向的数据。
operator->() 重载 -> 号,当智能指针指向的数据类型为自定义的结构体时,通过 -> 运算符可以获取其内部的指定成员。
swap() 交换 2 个相同类型 shared_ptr 智能指针的内容。
reset() 当函数没有实参时,该函数会使当前 shared_ptr 所指堆内存的引用计数减 1,同时将当前对象重置为一个空指针;当为函数传递一个新申请的堆内存时,则调用该函数的 shared_ptr 对象会获得该存储空间的所有权,并且引用计数的初始值为 1。
get() 获得 shared_ptr 对象内部包含的普通指针。
use_count() 返回同当前 shared_ptr 对象(包括它)指向相同的所有 shared_ptr 对象的数量。
unique() 判断当前 shared_ptr 对象指向的堆内存,是否不再有其它 shared_ptr 对象再指向它。
operator bool() 判断当前 shared_ptr 对象是否为空智能指针,如果是空指针,返回 false;反之,返回 true。

除此之外,C++11 标准还支持同一类型的 shared_ptr 对象,或者 shared_ptr 和 nullptr 之间,进行 ==,!=,<,<=,>,>= 运算。

主要用到的是use_count()和reset()。最后一个 operator bool(),这是类型转换。

自定义释放规则

对于在类中开辟堆内存的情况:

  1. 可以在类中析构中释放(因为智能指针计数为0时实际就是调用的类的析构);
  2. 可以自定义释放规则;
  3. 可以将类中的指针也定义为智能指针;

先看一段代码:

//定义了一个类,类中开辟了堆内存,用p指向
class tmpA
{
public:
	tmpA():p(new int(10)){}
	int* p;
};
void test()
{
    shared_ptr<tmpA> p1(new tmpA);//创建一个指向tmpA类型的智能指针
	int* p2 = p1->p;//保存p1指向的对象中的p的值
    cout << "对象释放前: *p2 = " << p2 << endl;
	p1.reset();//引用计数-1 =0 , 释放p1指向的空间
	cout << "对象释放后:p1 = " << p1 << endl;//看是否释放成功(是否为0)
    cout << "对象释放后:*p2 = " << *p2 << endl;//看对象中开辟的堆空间是否释放
}

结果:

在这里插入图片描述

这显然没有释放类中的堆内存

  1. 类析构中释放
//定义了一下析构函数
class tmpA
{
public:
	tmpA():p(new int(10)){}
	~tmpA() {
		if (p)
		{
			delete p;
			p = nullptr;
		}
	}
	int* p;
};

结果:

释放了

  1. 自定义释放规则
void test()
{
	shared_ptr<tmpA> p1(new tmpA,
		[](tmpA* cptr) {
			if (cptr->p) {
				delete cptr->p;
				cptr->p = nullptr;
			}
		}
		);//创建一个指向tmpA类型的智能指针,并自定义释放规则(这里用了lambda表达式)
	int* p2 = p1->p;//保存p1指向的对象中的p的值
	cout << "对象释放前: *p2 = " << *p2 << endl;
	p1.reset();//引用计数-1 =0 , 释放p1指向的空间
	cout << "对象释放后:p1 = " << p1 << endl;//看是否释放成功(是否为0)
	cout << "对象释放后:*p2 = " << *p2 << endl;//看对象中开辟的堆空间是否释放
    delete p2;
    p2 = nullptr;
}

结果:

在这里插入图片描述

释放了

  1. 类内指向堆内存的指针定义为智能指针
class tmpA
{
public:
	tmpA():p(new int(10)){}
	shared_ptr<int> p;
};
void test()
{
    shared_ptr<tmpA> p1(new tmpA);//创建一个指向tmpA类型的智能指针
	int* p2 = p1->p.get();//保存p1指向的对象中的p的值(从shared<int>转到int* 要用get()函数)
	cout << "对象释放前: *p2 = " << *p2 << endl;
	p1.reset();//引用计数-1 =0 , 释放p1指向的空间
	cout << "对象释放后:p1 = " << p1 << endl;//看是否释放成功(是否为0)
	cout << "对象释放后:*p2 = " << *p2 << endl;//看对象中开辟的堆空间是否释放
    delete p2;
    p2 = nullptr;
}

结果:

在这里插入图片描述

释放了。

其实也可以用对象中开辟的堆内存初始化一个智能指针,也就是把int p2 改成 shared p2*

这其实也是第三种方法

综上

智能指针的使用要理解其中原理,不然很容易造成过度释放。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11 谈谈shared_ptr 的相关文章

  • C# 中的 Stack<> 实现

    我最近一直在实现递归目录搜索实现 并且使用堆栈来跟踪路径元素 当我使用 string Join 连接路径元素时 我发现它们被颠倒了 当我调试该方法时 我查看了堆栈 发现堆栈内部数组中的元素本身是相反的 即最近 Push 的元素位于内部数组的
  • Boost ASIO 串行写入十六进制值

    我正在使用 ubuntu 通过串行端口与设备进行通信 所有消息都必须是十六进制值 我已经在 Windows 环境中使用白蚁测试了通信设置 并得到了我期望的响应 但在使用 Boost asio 时我无法得到任何响应 以下是我设置串口的方法 b
  • 如何在 C# 中将 Json 转换为对象

    我想将 Json 转换为 C 中的对象 这里的 Json 是 值 e920ce0f e3f5 4c6f 8e3d d2fbc51990e4 如何使用 Object 问题看似愚蠢 但其实并不那么愚蠢 我没有简单的 Json 我有 IEnume
  • C# 中一次性对象克隆会导致内存泄漏吗?

    检查这个代码 class someclass IDisposable private Bitmap imageObject public void ImageCrop int X int Y int W int H imageObject
  • Selenium - C# - Webdriver - 无法找到元素

    在 C 中使用 selenium 我试图打开浏览器 导航到 Google 并找到文本搜索字段 我尝试下面的 IWebDriver driver new InternetExplorerDriver C driver Navigate GoT
  • Android NDK 代码中的 SIGILL

    我在市场上有一个 NDK 应用程序 并获得了有关以下内容的本机崩溃报告 SIGILL信号 我使用 Google Breakpad 生成本机崩溃报告 以下是详细信息 我的应用程序是为armeabi v7a with霓虹灯支持 它在 NVIDI
  • Unity手游触摸动作不扎实

    我的代码中有一种 错误 我只是找不到它发生的原因以及如何修复它 我是统一的初学者 甚至是统一的手机游戏的初学者 我使用触摸让玩家从一侧移动到另一侧 但问题是我希望玩家在手指从一侧滑动到另一侧时能够平滑移动 但我的代码还会将玩家移动到您点击的
  • Libev,如何将参数传递给相关回调

    我陷入了 libev 中争论的境地 通常 libev 在类似的函数中接收包 接收回调 没关系 但是实际操作中 我们需要派遣一个亲戚 写回调 根据收到的包裹处理具体工作 例如 S RECV MSG pstRecvMsg S RECV MSG
  • 保证复制省略是否适用于函数参数?

    如果我理解正确的话 从 C 17 开始 这段代码现在要求不进行任何复制 Foo myfunc void return Foo auto foo myfunc no copy 函数参数也是如此吗 下面的代码中的副本会被优化掉吗 Foo myf
  • 条件类型定义

    如果我有一小段这样的代码 template
  • 让网络摄像头在 OpenCV 中工作

    我正在尝试让我的网络摄像头在 Windows 7 64 位中的 OpenCV 版本 2 2 中捕获视频 但是 我遇到了一些困难 OpenCV 附带的示例二进制文件都无法检测到我的网络摄像头 最近我发现这篇文章表明答案在于重新编译一个文件 o
  • 对于 C# Express 用户来说,有哪些好的工具可以识别可能重复的代码? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 也可以看看 有什么工具可以检查重复的 VB NET 代码吗 https stackoverflow c
  • MySQL 连接器 C++ 64 位在 Visual Studio 2012 中从源代码构建

    我正在尝试建立mySQL 连接器 C 从源头在视觉工作室2012为了64 bit建筑学 我知道这取决于一些boost头文件和C 连接器 跑步CMake生成一个项目文件 但该项目文件无法编译 因为有一大堆非常令人困惑的错误 这些错误可能与包含
  • 读取依赖步行者输出

    I am having some problems using one of the Dlls in my application and I ran dependency walker on it i am not sure how to
  • 构建 C# MVC 5 站点时项目之间的处理器架构不匹配

    我收到的错误如下 2017 年 4 月 20 日构建 13 23 38 C Windows Microsoft NET Framework v4 0 30319 Microsoft Common targets 1605 5 警告 MSB3
  • 如何编写一个接受 int 或 float 的 C 函数?

    我想用 C 语言创建一个扩展 Python 的函数 该函数可以接受 float 或 int 类型的输入 所以基本上 我想要f 5 and f 5 5 成为可接受的输入 我认为我不能使用if PyArg ParseTuple args i v
  • 在 C# 的 WebAPI 中的 ApiController 上使用“传输编码:分块”提供数据

    我需要服务分块传输使用编码数据API控制器 因为我无权访问HttpContext or the Http请求 我有点不知道在哪里写入响应以及在哪里刷新它 设置如下 public class MyController ApiControlle
  • 任何人都可以清楚地告诉如何在不使用像 这样的预定义函数的情况下找到带有小数值或小数值的指数吗? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 例如 2 0 5 1 414 所以想要 我是 c 的新手 所以请解释简单的逻辑 如果不是复杂的逻辑也足够了 在数学中 从整数取幂到实数
  • 如何组合两个 lambda [重复]

    这个问题在这里已经有答案了 可能的重复 在 C 中组合两个 lambda 表达式 https stackoverflow com questions 1717444 combining two lamba expressions in c
  • winform c# 中的弹出窗口

    我正在开发一个需要弹出窗口的项目 但问题是我还希望能够通过表单设计器在此弹出窗口中添加文本框等 所以基本上我有一个按钮 当您单击它时 它将打开我在表单设计器中设计的另一个窗口 我一直在谷歌搜索 但还没有找到我需要的东西 所以我希望你们能帮助

随机推荐

  • 新手入门Java企业开发,学习技术路线分享

    小白入门Java企业开发 学习技术路线分享 前言 学习开发在社会人群中主要有几类人群一类是以技术为生 一类是兴趣爱好人群还有一类是工作技术协助实现 本文主要提供给那些准备学习编程 入行编程的人群 希望能通过本文提供大家一个学习的路线能实现各
  • 数据库某列数据相乘

    1 基本思路 Oracle MySQL等数据库中只有sum max min等函数用于做某列数据聚合 而没有办法直接计算某列数据的乘积 所以需要另想办法 根据数学对数的加法原理 可对该列中所有数据取对数 后sum再做指数运算 即可得出所需结果
  • CDN加速的域名如何查找真实IP

    步骤一 通过该站点查找域名解析的历史记录 https toolbar netcraft com site report url xxx xxx com 步骤二 通过C段扫描来查找其真实的IP地址
  • jenkins-1.59+sonarqube-6.1+sonar-scanner-2.8+hg-3.9.2+maven-3.3.9+shell检查打包编译java项目

    本篇介绍centos7上以下几点的安装 1 sonarqube的安装 2 sonar scanner安装 3 mysql5 7的安装 4 jenkins sonarqube sonar scanner hg maven持续集成部署 官网下载
  • ps制作动态html,PS制作动态海报教程

    PS制作动态海报教程 4月 20 2019 发表于 视觉设计 评论 Sponsor 在新媒体时代 动态海报已经是平面设计师必备技能 C4D和AE如今已经变成设计软件的中坚力量 但是对于平面设计来说 这些软件还是门槛太高 那该怎么办呢 今天和
  • 19黑马笔记之二叉树的创建

    19黑马笔记之二叉树的创建 1 思想 一个一个节点的创建 先从根节点开始 若输入为 则该节点为空 若不是 则再次调用函数 给该节点创建左右孩子 最后返回该节点 2 实现代码 并不是很常用 了解一下即可 define CRT SECURE N
  • 如何使用streamlit实现端口扫描结果的可视化

    要使用Streamlit实现端口扫描结果的可视化 可以使用Python的第三方库pympler来分析内存使用情况 以下是一个简单的示例代码 from pympler import asizeof import streamlit as st
  • AIGC入门须知

    布道 AI 让更多普通人意识到新时代已经到来 毕竟早人一步就是红利 一 GPT 介绍 一 GPT 概述 GPT 是一种自然语言处理技术的聊天机器人 它能够实现智能对话 回答用户提问 完成任务等功能 具体来说 GPT 能够通过学习语言模式 理
  • 【前端面试题——JS篇】

    目录 1 javascript都有哪些数据类型 如何存储的 2 判断数据类型的方法有哪些 有什么区别 3 说说你对事件循环的理解 4 说说你对BOM的理解 BOM的核心都有哪些 作用是什么 5 Bind call apply有什么区别 如何
  • Python进阶-----面对对象7.0(细谈__new__方法和__init__方法)

    目录 前言 init 方法 new 方法 重点 1 new 方法的调用过程 2 重写 new 方法 3 new 方法不同返回值的情况 3 单例模式 前言 上一期初步介绍了 new 方法 但是实际上这个方法还有非常多的内容值得去讲一讲 学会了
  • Qt编译没使用Q_OBJECT导致编译出错,然后加入后编译仍出错的解决方法。

    这个问题 困扰我一下午 之前没加Q OBJECT导致不能使用信号和槽功能 导致我的程序已知编译出错 后来发现加上后 还是不能编译成功 继续出错 最后在overfolow stack上面找到了答案 原因首先是编译时没加Q OBJECT导致编译
  • Loss和神经网络训练

    出处 http blog csdn net han xiaoyang article details 50521064 声明 版权所有 转载请联系作者并注明出处 1 训练 在前一节当中我们讨论了神经网络静态的部分 包括神经网络结构 神经元类
  • 个人学习网站指南

    个人学习网站指南 一 书籍 二 视频 三 教程 四 实用 五 工具 六 行业 七 百科综合 八 博客论坛 九 官网 十 学习导航 个人平时使用到的一些网站 以免费为主 主要是为了自己以后需要用到能快速导航 持续更新 一 书籍 Free Co
  • [696]我的足迹精华贴@CSDN年度之“战”

    今天是2020年12月24日 距离2021年仅剩7天 临近年末 休几天假 看到CSDN新的征文活动 在犹豫中还是决定写一写年度总结 回顾一下2020 展望一下2021 CSDN血缘 那就先从如何与CSDN有了血缘关系说起吧 先聊聊我参与过的
  • 优秀笔记软件盘点—好看、强大的可视化笔记软件、知识图谱工具

    只推荐优质应用 推荐真正的思维工具 Heptabase 氢图 Walling Reflect InfraNodus TiddlyWiki FlowUs Heptabase 介绍 一款融合白板的可视化卡片笔记 优点 Hepta 提供了多面一体
  • stm32固件库(STM32F10x标准外设库)V3.5简介

    STM32F固件库是根据CMSIS ARM Cortex微控制器软件接口标准 而设计的 CMSIS标准由ARM和芯片生产商共同提出 让不同的芯片公司生产的Cortex M3微控制器能在软件上基本兼容 STM32F10x的固件库是一个完整的软
  • FFmpeg推送的流,播放端播放时花屏问题总结

    1 前言 很多次遇到了这种问题 在这里做一下总结 2 问题总结 第一 是播放端读取缓冲区不够 有时候 高清码流 一帧就好几兆 所以缓冲区要足够长 对于基于FFmpeg的播放器 应该是下面的参数 av dict set this gt opt
  • Lunix下软件安装

    1 Tarball 的产生和使用 1 1 make与configure make是能够对文件进行编译的简化指令 通常软件开发商都会写一支文件名为 configure 或者是 config 的侦测程序来侦测用户的作业环境 侦测完毕后主动的建立
  • Arduino:设置ADC参考电压

    使用LM35模组进行温度传感时 忘记设置参考电压 默认使用当前Arduino工作电压作为参考电压 但是 当用不稳定的外部电源供电时 输出值就不准确了 这时 最好使用内部参考电压 看下面的程序 int LM35 A0 void setup p
  • C++11 谈谈shared_ptr

    C 11 谈谈shared ptr 细节 个人用 十分主观 仅供参考 shared ptr是C 11中加入的一种智能指针 其实并不够智能 其作用就是帮助我们管理在堆中开辟的空间 避免野指针等众多内存管理不当造成的问题 重点 智能指针会自动的