C++11的shared_ptr共享的智能指针

        智能指针是存储指向动态分配(堆)对象指针的类。用于生存期控制,能够确保在离开指针所在作用域时,自动正确的销毁动态分配的对象,防止内存泄漏。它的一种通用实现技术是使用引用计数。美使用它一次,内部的引用计数加1,每析构一次,内部引用计数减1,减为0时,删除所指向的堆内存。

shared_ptr共享的智能指针

        std::shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。在最后一个shared_ptr析构的时候,内存才会被释放。

shared_ptr的基本用法

初始化

        可以通过构造函数,std::make_shared<T>辅助函数和reset方法来初始化shared_ptr,代码如下:

#include <memory>
#include <iostream>
using namespace std;int main()
{std::shared_ptr<int> p(new int(1));std::shared_ptr<int> p2 = p;std::shared_ptr<int> ptr;ptr.reset(new int(1));if (ptr){cout << "ptr is not null" << endl;}///编译报错,不允许直接赋值///std::shared_ptr<int> p4 = new int(1);return 0;
}

        我们应该优先使用make_shared来构造智能指针,因为它更加高效。

        不能将一个原始指针直接赋值给一个指针指针,例如,下面这种方法是错误的:

///编译报错,不允许直接赋值
std::shared_ptr<int> p = new int(1);

        可以看到智能指针的用法和普通指针的用法类似,只不过不需要自己管理分配的内存。shared_ptr不能通过直接将原始指针赋值来初始化,需要通过构造函数和辅助方法来初始化。对于一个未初始化的智能指针,可以通过reset方法来初始化,当智能指针中有值的时候,调用reset会使引用计数减1。另外,智能指针可以通过重载的bool类型符来判断智能智能中是否为空(未初始化)。

获取原始指针

        当需要获取原始指针时,可以通过get方法来返回原始指针,代码如下:

#include <memory>
#include <iostream>
#include <string>
using namespace std;int main()
{std::shared_ptr<int> p(new int(1));int* pp = p.get();cout << *pp << endl;std::shared_ptr<string> pstr(new string("hello world"));auto ppstr = pstr.get();cout << *ppstr << endl;return 0;
}

指定删除器

        智能指针初始化可以指定删除器,代码如下:

#include <memory>
#include <iostream>
#include <string>
#include <string.h>
using namespace std;typedef struct Message
{char*   ptr;int     i;double  db;
}Message;void DeletePtr(Message* pm)
{cout << pm->i << endl;cout << pm->db << endl;cout << pm->ptr << endl;if (pm->ptr){delete [] pm->ptr;pm->ptr = nullptr;}
}int main()
{Message* m = new Message;m->db = 2.8;m->i = 1;m->ptr = new char[100];memset(m->ptr, 0, 100);char* ptr = (char*)"hello world";strncpy(m->ptr, ptr, strlen(ptr));std::shared_ptr<Message> p(m, DeletePtr);///std::shared_ptr<Message> pp(m, [](Message* pm){if (pm->ptr) { cout << pm->ptr << endl; delete [] pm->ptr;}});return 0;
}

        当p的引用计数为0时,自动调用删除器DeletePtr来释放对象的内存。删除器可以是一个lambda表达式,因此,上面的写法还可以改为:

std::shared_ptr<Message> pp(m, [](Message* pm){if (pm->ptr) { cout << pm->ptr << endl; delete [] pm->ptr;}});

使用shared_ptr需要注意的问题

        智能指针虽然能自动管理堆内存,但是它哟不少缺陷,在使用时需要注意。

        1、不要用一个原始指针初始化多个shared_ptr,例如下面这些是错误的:

int* ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr);    ///logic error

        2、不要在函数实参中创建shared_ptr,对于下面的写法是错误的:

function(shared_ptr<int>(new int), g());

        因为C++的函数参数的计算顺序在不同的编译器不同的调用约定下可能是不一样的,一般是从右到左,但也有可能是从左到右,所以,可能的过程是先new int,然后调用g(),如果恰好g()发生异常,而shared_ptr<int>还没有创建,则int内存泄漏,正确的写法是先创建智能指针,代码如下:

shared_ptr<int> p(new int);
f(p, g());

        3、通过shared_from_this()返回this指针。不要将this指针作为shared_ptr返回出来,因为this指针本质上是一个裸指针,因此,这样可能会导致重复析构,看下面的代码:

#include <memory>
#include <iostream>
#include <string>
#include <string.h>
using namespace std;struct A
{shared_ptr<A> GetSelf(){return shared_ptr<A>(this);}///析构函数调用了2次~A(){cout << "delete A" << endl;}
};int main()
{shared_ptr<A> sp1(new A);shared_ptr<A> sp2 = sp1->GetSelf();return 0;
}

        在这个例子中,由于用同一个指针(this)构造了两个智能指针sp1和sp2,而他们之间是没有任何关系的,在离开作用域之后this将会被构造的两个智能指针各自析构,导致重复析构的错误。

        正确返回this的shared_ptr的做法是:让目标类通过派生std::enable_shared_this<T>类,然后使用基类的成员函数shared_from_this来返回this的shared_ptr,看下面的代码:

#include <memory>
#include <iostream>
#include <string>
#include <string.h>
using namespace std;struct A : public std::enable_shared_from_this<A>
{shared_ptr<A> GetSelf(){return shared_from_this();}///析构函数调用了1次~A(){cout << "delete A" << endl;}
};int main()
{shared_ptr<A> sp1(new A);shared_ptr<A> sp2 = sp1->GetSelf();return 0;
}

        4、要避免循环引用。指针指针最大的一个陷阱是循环引用,循环引用会导致内存泄漏。下面是一个典型的循环引用的场景。

#include <memory>
#include <iostream>
#include <string>
#include <string.h>
using namespace std;struct A;
struct B;struct A
{std::shared_ptr<B> bptr;~A(){cout << "A is deleted" << endl;}
};struct B
{std::shared_ptr<A> aptr;~B(){cout << "B is deleted" << endl;}
};int main()
{{///析构函数没有被调用shared_ptr<A> ap(new A);shared_ptr<B> bp(new B);ap->bptr = bp;bp->aptr = ap;}return 0;
}

        测试结果是两个指针A和B都不会被删除,存在内存泄漏。循环引用导致ap和bp引用计算器为2,在离开作用域之后,ap和bp的引用计数器减为1,并不会为0,导致两个指针不会被析构,产生了内存泄漏。

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

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

相关文章

C++ 设计原则 - 依赖倒置原则

C 中的依赖倒置原则&#xff08;Dependency Inversion Principle&#xff0c;DIP&#xff09;是SOLID设计原则中的一部分&#xff0c;它要求高层模块不应该依赖于低层模块&#xff0c;二者都应该依赖于抽象&#xff1b;而抽象不应该依赖于具体实现细节&#xff0c;具体实现细节…

08.智慧商城——购物车布局、全选反选、功能实现

01. 购物车 - 静态布局 基本结构 <template><div class"cart"><van-nav-bar title"购物车" fixed /><!-- 购物车开头 --><div class"cart-title"><span class"all">共<i>4</i>件商品…

记录一次macos没有sudoers文件问题

问题&#xff1a;但我输入sudo visudo sudo: unable to stat /etc/sudoers: No such file or directory sudo: no valid sudoers sources found, quitting sudo: error initializing audit plugin sudoers_audit解决: 此文件不存在 su root # 现在我能登录root touch /etc/su…

10-19 HttpServletResponse

相应的对象 web开发模型&#xff1a;基于请求与相应的模型 一问一答的模型 Response对象:响应对象,封装服务器给客户端的相关的信息 顶级接口: ServletResponse 父接口:HttpServletResponse response对象的功能分为以下四种:(都是服务器干的事注意) 设置响应头信息; 发送状态码…

EtherCAT主站SOEM -- 6 -- SOEM之ethercatcoe.h/c(ethercateoe/foe/soe)文件解析

EtherCAT主站SOEM -- 6 -- SOEM之ethercatcoe.h/c-ethercateoe/foe/soe 0 COE,EOE,FOE,SOE区别:0.1 ehercatcoe.h/c,ethercateoe.h/c,ethercatfoe.h/c及ethercatsoe.h/c一 ethercatcoe.h/c文件功能预览:二 ethercatcoe.h/c 文件的主要函数的作用:2.1.1 void ec_SDOerro…

Vue2基础-Vue对象进阶介绍1

文章目录 一、绑定样式绑定class样式绑定style样式总结 二、渲染条件渲染列表渲染语法key详解 三、Vue检测数据原理问题解决 四、收集表单数据五、过滤器定义语法: 六、内置指令回顾v-text指令:v-htmlcookie问题 v-clock指令v-oncev-pre 一、绑定样式 绑定class样式 <!-- …

使用Flink处理Kafka中的数据_题库子任务_Java语言实现

2024年职业院校技术大赛-高职大数据应用开发赛项专题。 使用Flink处理Kafka中的数据_题库子任务1、2、3_Java语言实现使用Flink处理Kafka中的数据_题库子任务4、5、6_Java语言实现使用Flink处理Kafka中的数据_题库子任务7、8、9_Java语言实现

互联网摸鱼日报(2023-11-20)

互联网摸鱼日报(2023-11-20) 36氪新闻 2023年11月山东新三板企业市值TOP100&#xff1a;21家企业冲击北交所 2024年企业数字化转型工作展望 本周双碳大事&#xff1a;中美发表阳光之乡声明&#xff1b;CCER三大配套制度发布&#xff1b;国鸿氢能通过港交所上市聆讯 基因编辑…

python脚本获取cookie写入本地

自己负责的一个项目接口有token验证&#xff0c;登录功能&#xff08;获取token&#xff09;是相当于一个通用的第三方&#xff0c;点击登录按钮跳转公共的登录平台&#xff0c;登录完后再跳转回来。所以导致本地开发的时候&#xff0c;无法登录完后&#xff0c;直接跳回本地页…

SpringCloud-Gateway修改Response响应体,并解决大数据量返回不全等问题

官网相关案例&#xff1a; Spring Cloud Gatewayhttps://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-modifyresponsebody-gatewayfilter-factory ModifyRequestBodyGatewayFilterFactory类: https://github.com/spring-cloud/spring-cloud-gate…

Virtual安装centos后,xshell连接centos

1. 网络使用Host-Only模式动态分配IP&#xff0c;点确定后&#xff0c;centos 上运行 system restart network &#xff0c;使用ifconfig查看新的ip&#xff0c;XShell可以直接连上centos&#xff0c; 但是由于使用的是Host-Only模式&#xff0c;centos不能访问网络&#xff0c…

【解锁未来】OpenAI:从创始到GPT4的发展

文章目录 创始发展GPT-4的发布 创始 让我们回到2015年&#xff0c;当时一群有远见的科技领袖&#xff0c;包括山姆柯曼、伊隆马斯克等人&#xff0c;共同创立了OpenAI。他们希望通过OpenAI&#xff0c;推动人工智能的发展&#xff0c;同时确保这项技术能够造福全人类。 发展 …

​软考-高级-系统架构设计师教程(清华第2版)【第9章 软件可靠性基础知识(P320~344)-思维导图】​

软考-高级-系统架构设计师教程&#xff08;清华第2版&#xff09;【第9章 软件可靠性基础知识&#xff08;P320~344&#xff09;-思维导图】 课本里章节里所有蓝色字体的思维导图

解决在pycharm中使用matplotlib画图问题

第一&#xff0c;再导入包后直接绘图出现&#xff1a; AttributeError: module backend_interagg has no attribute FigureCanvas表明版本不兼容&#xff0c;我们需要加入&#xff1a;matplotlib.use(‘TkAgg’) 导入函数就变成了&#xff1a; import matplotlib matplotlib.…

JAXB的XmlElement注解

依赖 如果基于JAX-WS开发&#xff0c;可以在maven工程的pom.xml文件中增加如下依赖&#xff0c;会将依赖的JAXB库也下载下来&#xff1a; <dependency><groupId>jakarta.xml.ws</groupId><artifactId>jakarta.xml.ws-api</artifactId><vers…

Django_学习_02

路由系统 urls 视图 views 模板 TempLates 静态文件和媒体文件 static 中间件 middleware ORM databases 1.路由系统 a.传统路…

基础课8——中文分词

中文分词指的是将一个汉字序列切分成一个一个单独的词。分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。在英文的行文中&#xff0c;单词之间是以空格作为自然分界符的&#xff0c;而中文只是字、句和段能通过明显的分界符来简单划界&#xff0c;唯独词没有一个…

开源与闭源:创新与安全的平衡

目录 一、开源和闭源的优劣势比较 一、开源软件的优劣势 优势 劣势 二、闭源软件的优劣势 优势 劣势 二、开源和闭源对大模型技术发展的影响 一、机器学习领域 二、自然语言处理领域 三、数据共享、算法创新与业务拓展的差异 三、开源与闭源的商业模式比较 一、盈…

C#入门(10):集合用法介绍

在C#中&#xff0c;集合是一种特殊的数据类型&#xff0c;允许我们将多个元素组织在一起。这些元素可以是相同的类型或者可以是不同的类型。C#集合主要包括以下几种类型&#xff1a; List&#xff1a;它是一个有序的元素列表&#xff0c;用户可以添加、删除或查找元素。Dictio…

【数据结构】详解链表结构

目录 引言一、链表的介绍二、链表的几种分类三、不带头单链表的一些常用接口3.1 动态申请一个节点3.2 尾插数据3.3 头插数据3.4 尾删数据3.5 头删数据3.6 查找数据3.7 pos位置后插入数据3.8 删除pos位置数据3.9 释放空间 四、带头双向链表的常见接口4.1创建头节点&#xff08;初…