实例代码

// #include <iostream>
#include <vector>
#include <map>
#include <string>
#include <memory>using namespace std;shared_ptr<int> create0(int value){return make_shared<int>(value); //返回一个shared_ptr
}void myfunc(int value){shared_ptr<int> ptmp = create0(10);return; // 离开作用域后, ptemp会被自动释放,它所指向的内存也会被自动释放
}shared_ptr<int> myfunc1(int value){shared_ptr<int> ptmp = create0(10);return ptmp; // 系统是根据ptmp这个局部变量来产生一个临时的shared_ptr对象往回返
}void proc(shared_ptr<int> ptr){return;
}class CT {public:shared_ptr<CT> getself(){return shared_ptr<CT>(this); //用裸指针初始化了多个shared_ptr的感觉;}
};class CT1 : enable_shared_from_this<CT1>  {public:shared_ptr<CT1> getself(){return shared_from_this(); // 这个就是enable_shared_from_this 类模板}
};class CB; //声明一下CBclass CA{public:shared_ptr<CB> m_pbs;~CA(){int test;test = 1;}
};class CB{public:shared_ptr<CA> m_pas;~CB(){int test;test = 1;}
};class CB1; //声明一下CBclass CA1{public:shared_ptr<CB1> m_pbs;~CA1(){int test;test = 1;}
};class CB1{public:weak_ptr<CA1> m_pas;  //把这里变成弱引用;~CB1(){int test;test = 1;}
};int main()
{//一、std::shared_ptr 使用场景//myfunc(12); // 如果这块不用shared_ptr变量来接收myfunc返回的结果,那么从myfunc返回的shared_ptr就会被销毁,所指向的对象也会被销毁。 auto p11 = myfunc1(12); // 我们用了一个变量来接myfunc的返回值,那么myfunc1返回的shared_ptr就不会被销毁,它所指向的对象也不会被销毁。//二、std::shared_ptr 使用陷阱分析//   (2.1) 慎用裸指针int *p = new int(100); //裸指针//proc(p); //语法错, int* p 不能转换成shared_ptr<int>shared_ptr<int> p2(p);proc(p2);proc(shared_ptr<int>(p)); // 参数是个临时的shared_ptr, 用一个裸指针显式的构造;*p = 45; // 潜在的不可预料的问题; 因为p指向的内存已经被释放了;*p2 = 45;//把一个普通裸指针绑定到了一个shared_ptr上之后,那内存管理的责任就交给了shared_ptr了,这个时候你就不应该再//用裸指针(内置指针)来访问shared_ptr 所指向的内存了;shared_ptr<int> myp(new int(100));proc(myp);*myp = 45;//绝对要记住,不要用裸指针初始化多个shared_ptrint *p1 = new int(100); // 裸指针shared_ptr<int> p3(p1);shared_ptr<int> p4(p1); // p1 和 p4 无关联了(p3和p4每个强引用计数都为1了),会导致p3和p4所指向的内存被释放两次,产生异常shared_ptr<int> p5(new int);shared_ptr<int> p6(p5); // 这种写法就是ok的,p5和p6 指向同一个内存地址并且两者是互通(用的是同一个控制块)//   (2.2) 慎用get()返回的指针//返回智能指针指向的对象所对应的裸指针(有些函数接口可能只能使用裸指针)//get返回的指针不能delete,否则会异常shared_ptr<int> myp1(new int(100));int *p00 = myp1.get();//delete p00; // 不可以删除,会导致异常//不能将其他智能指针绑定到get返回的指针上shared_ptr<int> myp2(new int(100));int *p22 = myp2.get(); //这个指针千万不能随意释放,否则myp2就没办法正常管理该指针了{shared_ptr<int> myp3(p22); // 现在myp2和myp3引用计数都为1,但一旦跳出这个程序块shared_ptr<int> myp4;myp4 = shared_ptr<int>(p22);shared_ptr<int> myp5(myp2);}//离开上边这个myp3的范围,导致myp2指向的内存被释放了;*myp2 = 65; // 该内存已经被释放,这样赋值会导致不可预料的后果;//结论:永远不要用get得到的指针来初始化另外一个智能指针或者给另外一个智能指针赋值。//   (2.3) 不要把类对象指针(this) 作为shared_ptr返回,改用enable_shared_from_thisshared_ptr<CT> pct1(new CT);//shared_ptr<CT> pct2 = pct1; //这是两个强引用;shared_ptr<CT> pct2 = pct1->getself(); //问题出现;//用到C++标准库里边的类模板: enable_shared_from_this://现在,在外面创建CT对象的智能指针以及通过CT对象返回的this智能指针都是安全的;//这个enable_shared_from_this中有一个弱指针weak_ptr,这个弱指针能够监视this,// 在我们调用shared_from_this()这个方法时,这个方法内部实际上是调用了这个weak_ptr的lock()方法;// 大家都知道lock方法会让shared_ptr指针计数+1, 同时返回这个shared_ptr , 这个就是工作原理;//     (2.4) 避免循环引用shared_ptr<CA> pca(new CA);shared_ptr<CB> pcb(new CB);pca->m_pbs = pcb; // 等价于指向CB对象的有两个强引用pcb->m_pas = pca; // 等价于指向CA对象的有两个强引用shared_ptr<CA1> pca1(new CA1);shared_ptr<CB1> pcb1(new CB1);pca1->m_pbs = pcb1; // 等价于指向CB1对象的有两个强引用pcb1->m_pas = pca1; // 因为m_pas 是弱引用,所以这里指向CA1的对象只有一个强引用;// 离开作用域之后,pca引用计数从1 就变成0 会释放CA1 对象(CA1的析构函数被执行);// CA1 的析构函数被执行了,(表示对象即将被释放),导致CA1内的m_pbs引用计数会减1, 也就是指向CB1对象的引用计数-1;// 超出pcb作用域时指向CB1 的计数也会-1;最终,会有一个时刻,指向CB1对象的强引用计数会从1减少到0,导致CB1对象被释放;//CA1先析构,CB1后析构;//三、性能说明//   (3.1) 尺寸问题//shared_ptr的尺寸是裸指针的2倍; weak_ptr的尺寸是裸指针的2倍;char *p111;int ilenp111 = sizeof(p111); // 4字节shared_ptr<string> p222;int ilenp222 = sizeof(p222); // 8字节 ,包含两个裸指针的//a). 第一个裸指针指向的是这个智能指针所指向的对象//b). 第二个裸指针 指向一个很大的数据结构(控制块),这个控制块里边有啥://b.1) 所指向对象的强引用计数: shared_ptr//b.2) 所指向对象的弱引用计数: weak_ptr//b.3) 其他数据,比如删除器指针,内存分配器;// 这个控制块是由第一个指向某个对象的shared_ptr来创建的;//控制块创建时机:// a) make_shared : 分配并初始化一个对象,返回指向此对象的shared_ptr,所以,这个make_shared它总是能够创建一个控制块shared_ptr<int> p333 = make_shared<int>(100);// b) 用裸指针来创建一个shared_ptr 对象时,int *pi111 = new int();shared_ptr<int> p555(pi111);shared_ptr<int> p666(pi111); // 不允许,否则会产生多个控制块,也就是多个引用计数(每个都是1)析构时析构多次,导致异常;shared_ptr<int> p777(new int);//   (3.2) 移动语义shared_ptr<int> p888(new int(100));shared_ptr<int> p999(std::move(p888)); //移动语义, 移动构造一个新的智能指针对象p2,// p1 就不再指向该对象(变成空),引用计数依旧是1;shared_ptr<int> p1000; p1000 = std::move(p999); // 移动赋值, p999指向空,p1000指向该对象,整个对象引用计数仍旧为1;//移动肯定比复制快,复制你要增加引用计数,移动不需要;//移动构造函数快过复制构造函数,移动赋值运算符快过拷贝赋值运算符;//四、 补充说明和使用建议// a). 掌握了绝大部分shared_ptr用法;小部分没讲解,靠大家摸索。// 分配器,解决内存分配问题;// shared_ptr<int> p(new int), mydeleter(), mymallocator<int>()); ....// b). 谨慎使用,凡是没讲到过的用法:// new shared_ptr<T> , memcpy() 奇怪用法,大家不要轻易尝试。// c). 优先使用make_shared()shared_ptr<string> ps1(new string("I Love China!")); // 分配两次内存auto ps2 = make_shared<string>("I Love China!");// 分配1次内存system("pause");
}

C++语法学习笔记三十九:shared_ptr使用场景、陷阱、性能分析、使用建议相关推荐

  1. C++语法学习笔记二十九: 详解decltype含义,decltype主要用途

    实例代码 // 详解decltype含义,decltype主要用途#include <iostream> #include <functional> #include < ...

  2. JAVA学习笔记(三十九)-打印流

    import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; i ...

  3. 【js学习笔记三十九】简单工厂模式

    目录 前言 导语 代码部分 总结 前言 我是歌谣 我有个兄弟 巅峰的时候排名c站总榜19 叫前端小歌谣 曾经我花了三年的时间创作了他 现在我要用五年的时间超越他 今天又是接近兄弟的一天人生难免坎坷 大 ...

  4. Mr.J-- jQuery学习笔记(三十二)--jQuery属性操作源码封装

    扫码看专栏 jQuery的优点 jquery是JavaScript库,能够极大地简化JavaScript编程,能够更方便的处理DOM操作和进行Ajax交互 1.轻量级 JQuery非常轻巧 2.强大的 ...

  5. python数据挖掘学习笔记】十九.鸢尾花数据集可视化、线性回归、决策树花样分析

    #2018-04-05 16:57:26 April Thursday the 14 week, the 095 day SZ SSMR python数据挖掘学习笔记]十九.鸢尾花数据集可视化.线性回 ...

  6. Python学习笔记(十九)面向对象 - 继承

    Python学习笔记(十九)面向对象 - 继承 一.继承的概念 # 继承:子类继承父类的所有方法和属性# 1. 子类 class A(object):def __init__(self):self.n ...

  7. tensorflow学习笔记(三十二):conv2d_transpose (解卷积)

    tensorflow学习笔记(三十二):conv2d_transpose ("解卷积") deconv解卷积,实际是叫做conv_transpose, conv_transpose ...

  8. javaweb学习总结(三十九)——数据库连接池

    javaweb学习总结(三十九)--数据库连接池 一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10 ...

  9. JavaScript学习(三十九)—对象中内容的操作

    JavaScript学习(三十九)-对象中内容的操作 一.对象中内容的操作:增.删.改.查 (一).增:给对象添加属性或者方法 1)方式1:对象名称.属性名=属性值: 2)方式2:对象名称['属性名' ...

  10. 孤傲苍狼 只为成功找方法,不为失败找借口! javaweb学习总结(三十九)——数据库连接池 一、应用程序直接获取数据库连接的缺点   用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要

    孤傲苍狼 只为成功找方法,不为失败找借口! javaweb学习总结(三十九)--数据库连接池 一.应用程序直接获取数据库连接的缺点 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对 ...

最新文章

  1. MySQL数据库test连接语句_【MySQL数据库开发之二】MySQL 基础语句的书写与操作!...
  2. 人群运动--Scene-Independent Group Profiling in Crowd
  3. sqlserver2000换成mysql_将Microsoft SQL Server 2000数据库转换成MySQL数据库
  4. 基于redis的cas集群配置(转)
  5. CI类实现session基本用法
  6. 一年后斩获腾讯T3,面试必问!
  7. rcu_assign_pointer、rcu_dereference、ACCESS_ONCE
  8. linux启动数据库orl,linux5.4下oracle问题
  9. 划船训练常见错误:含胸驼背肩胛骨活动不足
  10. cdr怎么做文字路径_整理128张图片,告诉你文字少的PPT应该怎么做?
  11. nginx main user
  12. C语言中的undefined behavior系列(2)-- lifetime of object
  13. 1079. Total Sales of Supply Chain (25)-求数的层次和叶子节点
  14. 量子计算机具有天然的处理能力,新发现:光纤可用于构建具有强大计算与处理能力的超导量子计算机...
  15. AndroidHttpClient详解
  16. Selenium Firefox Webdriver 无图模式
  17. 三次握手,为什么不是两次,也不是四次
  18. 项目管理提升绩效考核的方法
  19. eve手游服务器维护,EVE手游国际服新手教程,云手机小号多开辅助快速获得资源...
  20. c语言 code table,单片机C语言unsigned char code table是什么意思?

热门文章

  1. 中国农业大学计算机专业硕士研究生,中国农业大学计算机专业2020考研复试分数线...
  2. 在html中怎样加圆点,css伪类-小圆点
  3. excel高效之sumproduct()带权重计算 如:绩效
  4. 空城过客XP系统快捷方式去除箭头方法
  5. C++ 设计模式 面向对象设计原则 相关概念介绍(结合UnrealEngine4)
  6. 蓝桥杯算法提高 盾神与条状项链
  7. 一政网:公务员到底是做什么的?
  8. java 日程表_Java实现 LeetCode 729 我的日程安排表 I(二叉树)
  9. DPI vs DFI
  10. esxi01磁盘扩容_给EXSI虚拟机中linux硬盘扩容