shared_ptr的使用


分配内存

  • make_shared
//make_shared<int>分配一块int类型大小的内存,并值初始化为100
//返回值是shared_ptr类型,因此可以直接赋值给sp
shared_ptr<int> sp = make_shared<int>(100);
  • new

    接受指针参数的只能指针构造函数是explicit的,因此,我们不能将一个内置指针隐式转化为一个只能指针,必须使用直接初始化形式

//错误! 不会进行隐式转换,类型不符合
shared_ptr<int> sp1 = new int(100);
//正确,直接初始化调用构造函数
shared_ptr<int> sp2(new int(100000));

shared_ptr的操作

分配好了内存空间,我们就可以使用shared_ptr定义的操作了

  • p.get() 
    返回p保存的指针

  • swap(p,q) 
    交换p、q中保存的指针

  • shared_ptr<T> p(q) 
    p是q的拷贝,它们指向同一块内存,互相关联

  • p = q 
    用q为p赋值,之后p、q指向同一块内存,q引用计数+1,p(原来内存空间的)引用计数-1

  • p.use_count() 
    返回与p共享对象的智能指针数量

  • shared_ptr<T> p(q,d) 
    q是一个可以转换为T*的指针,d是一个可调用对象(作为删除器),p接管q所指对象的所有权,用删除器d代替delete释放内存

  • p.reset() 
    将p重置为空指针

  • p.reset(p) 
    将p重置为p(的值)

  • p.reset(p,d) 
    将p重置为p(的值)并使用d作为删除器


shared_ptr 关联与独立

多个共享指针指向同一个空间,它们的关系可能是关联(我们所期望的正常关系)或是独立的(一种错误状态)

    shared_ptr<int> sp1(new int(10));
shared_ptr<int> sp2(sp1), sp3;
sp3 = sp1;
//一个典型的错误用法
shared_ptr<int> sp4(sp1.get());
cout << sp1.use_count() << " " << sp2.use_count() << " "
<< sp3.use_count() << " " << sp4.use_count() << endl;
//输出 3 3 3 1

sp1,sp2,sp3是相互关联的共享指针,共同控制所指内存的生存期,sp4虽然指向同样的内存,却是与sp1,sp2,sp3独立的,sp4按自己的引用计数来关联内存的释放。

只有用一个shared_ptr为另一个shared_ptr赋值时,才将这连个共享指针关联起来,直接使用地址值会导致各个shared_ptr独立。


向shared_ptr传递删除器

有时候我们需要用智能指针管理非new的对象,或者是没有析构函数的类,由于shared_ptr默认使用delete来释放内存并执行析构函数,对于以上的两种情况是不适用的,所以我们要传递特别的删除器

删除器必须接受单个类型为 T* 的参数

//没有析构函数的类
struct MyStruct
{
int *p;
MyStruct():p(new int(10)) { } //构造函数中申请了一块内存
//用裸指针管理,不用时需要手动释放
}; void main()
{
//st是局部的对象,存放在栈区
//并非由new申请,不可用delete释放内存
MyStruct st;
//一个作用域
{
shared_ptr<MyStruct> sp(&st, [](MyStruct *ptr) {
delete(ptr->p);
ptr->p = nullptr;
cout << "destructed." << endl;
});
}
// 离开作用域,调用传递的删除器释放sp所指的内存空间
}

对于以上这个例子,首先不可以用delete来释放局部对象,然后MyStruct也没有析构函数来释放申请的空间,所以向管理它的shared_ptr传递一个删除器来做这两件事。



shared_ptr的陷阱


不要写出独立的shared_ptr

关于独立的shared_ptr的意思及危害上面已经说出,遵守下面几点来避免这个错误

  1. 不要与裸指针混用
//错误场景1
int *x(new int(10));
shared_ptr<int> sp1(x);
shared_ptr<int> sp2(x);
//虽然sp1、sp2都指向x所指的内存,但他们是独立的,
//会在其他shared_ptr还在使用内存的情况下就释放掉内存
//失去了设计共享指针的意义
//同时,使用裸指针x本身也是很危险的,x随时可能变成空悬指针而无从知晓
//错误场景2
//函数接受一个共享指针参数
void func(shared_ptr<int> sp); int *x(new int(10));
//创建了一个指向x指针所指内存的共享指针,引用计数为1,是引用这块内存的唯一共享指针
func(shared_ptr<int> (x));
//离开函数即离开共享指针的作用域,这块内存即被删除
  1. 不要用p.get()的返回值为shared_ptr赋值
shared_ptr<int> sp1(new int(10));
//sp2与sp1独立
shared_ptr<int> sp2(sp1.get()),sp3;
//sp3与sp1独立
sp.reset(sp1.get());

谨慎使用p.get()的返回值

p.get()的返回值就相当于一个裸指针的值,不合适的使用这个值,上述陷阱的所有错误都有可能发生,遵守以下几个约定

  1. 不要保存p.get()的返回值 
    无论是保存为裸指针还是shared_ptr都是错误的 
    保存为裸指针不知什么时候就会变成空悬指针 
    保存为shared_ptr则产生了独立指针

  2. 不要delete p.get()的返回值 
    会导致对一块内存delete两次的错误


记得向shared_ptr传递删除器

如果用shared_ptr管理非new对象或是没有析构函数的类时,应当为其传递合适的删除器


避免形成指针循环引用

shared_ptr的使用和陷阱的更多相关文章

  1. C++11智能指针原理和实现

    一.智能指针起因 在C++中,动态内存的管理是由程序员自己申请和释放的,用一对运算符完成:new和delete. new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针: delete:指向 ...

  2. C++智能指针的原理和实现

    一.智能指针起因 在C++中,动态内存的管理是由程序员自己申请和释放的,用一对运算符完成:new和delete. new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针: delete:指向 ...

  3. 学习笔记:shared_ptr陷阱

    条款1:不要把一个原生指针给多个shared_ptr管理 int* ptr = new int; shared_ptr<int> p1(ptr); shared_ptr<int> ...

  4. 智能指针shared_ptr的用法

    为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...

  5. c++ shared_ptr 使用注意事项. 1

    条款1:不要把一个原生指针给多个shared_ptr管理 int* ptr = new int; shared_ptr<int> p1(ptr); shared_ptr<int> ...

  6. 关于智能指针boost::shared_ptr

    boost库中的智能指针shared_ptr, 功能强大, 且开销小,故受到广大coder的欢迎. 但在实际的使用过程中,笔者也发现了一些不足. 1.定制的删除器 shared_ptr除了可以使用默认 ...

  7. C++ Primer : 第十二章 : 动态内存之shared_ptr与new的结合使用、智能指针异常

    shared_ptr和new结合使用 一个shared_ptr默认初始化为一个空指针.我们也可以使用new返回的指针来初始化一个shared_ptr: shared_ptr<double> ...

  8. c/c++ 智能指针 shared_ptr 和 new结合使用

    智能指针 shared_ptr 和 new结合使用 用make_shared函数初始化shared_ptr是最推荐的,但有的时候还是需要用new关键字来初始化shared_ptr. 一,先来个表格,唠 ...

  9. boost shared_ptr weak_ptr

    文档: http://www.boost.org/doc/libs/1_57_0/libs/smart_ptr/shared_ptr.htm shared_ptr构造有个原型 template< ...

随机推荐

  1. python算法与数据结构-插入排序算法(34)

    一.插入排序的介绍 插入排序的工作方式非常像人们排序一手扑克牌一样.开始时,我们的左手为空并且桌子上的牌面朝下.然后,我们每次从桌子上拿走一张牌并将它插入左手中正确的位置.为了找到一张牌的正确位置,我 ...

  2. JVM那些事儿之内存区域

    相信绝大多数java开发者或多或少的都应该知道jvm,但是有多少人又深入去了解过,笔者深感自身能力的不足,去看了些资料,觉得还是有必要整理下自己的学习记录,时常回头看看,多看多实践提升自己的能力,故开 ...

  3. 自定义创建vue文件代码块

    "vue-component": { "prefix": "vue-component", "body": [ &quo ...

  4. MenuOS扩展(课堂时间作业)

    MenuOS扩展实验 1.编译内核 2.制作根文件系统 rm menu -rf 强制删除原menu文件 git clone https://github.com/mengning/menu.git 从 ...

  5. el-select remote 远程搜索 多个共享一个options,options改变时输入框值不显示名称的问题

    原因:el-select remote 远程搜索 多个共享一个options,当使用 remote-method 请求数据后,options被改变,value的值不包含在options中,value的 ...

  6. 【Java】《Java程序设计基础教程》第五章学习

    5.1 抽象类 Java语言中,父类的某些方法不包括任何逻辑,并且需要由子类重写.在这种情况下,用abstract关键字来修饰一个类时,这个类叫做抽象类,用abstract关键字来修饰一个方法时,这个 ...

  7. java EL表达式中${param.name}详细

    在浏览器地址输入,表示传入一个参数test,值为123 URL:http://localhost:8888/Test/index.jsp?test=123 <body> ${test} $ ...

  8. bind、apply、call的理解

    一直感觉代码中有call和apply就很高大上(看不懂),但是都草草略过,今天非要弄明白!以前总是死记硬背:call.apply.bind 都是用来修改函数中的this,传参时,call是一个个传参, ...

  9. 使用Navicat Premium 12导出SQL语句并在Power Designer 16.5中生成物理模型

    内容简介 本文主要介绍使用Navicat Premium 12导出建表SQL(使用MySQL数据库)文件,并在Power Designer 16.5中使用导出的SQL文件来生成物理模型的步骤. 操作步 ...

  10. SpringBoot学习(四)开发web应用

    Spring Boot非常适合web应用程序开发.可以使用嵌入式Tomcat.Jetty.Undertow或Netty创建自包含的HTTP服务器.大多数web应用程序使用spring-boot-sta ...