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 的相关文章

随机推荐

  • 新手入门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中加入的一种智能指针 其实并不够智能 其作用就是帮助我们管理在堆中开辟的空间 避免野指针等众多内存管理不当造成的问题 重点 智能指针会自动的