一、std::shared_ptr使用场景

shared_ptr<int> create(int value)
{return make_shared<int>(value);  //返回一个shared_ptr
}shared_ptr<int> myfunc(int value)
{shared_ptr<int> ptmp = create(value);return ptmp;  //系统会根据ptmp这个局部变量产生一个临时的shared_ptr对象往回返
}void myfunc_void(int value)
{shared_ptr<int> ptmp = create(value);return;  //离开作用域后,ptmp会被自动释放,它所指向的内存也会被自动释放
}int main()
{myfunc_void(12);  //如果这块不用shared_ptr变量来接收myfunc返回的结果,那么从myfunc返回的shared_ptr就会被销毁。//所指向的对象也会被销毁。auto p1 = myfunc(10);  //我们用了一个变量来接myfunc的返回值,那么myfunc返回的shared_ptr就不会被销毁,//它所指向的对象也不会被销毁。return 0;
}

二、std::shared_ptr使用陷阱分析

<1>慎用裸指针

void proc(shared_ptr<int> value)
{return;
}int main()
{int* p = new int(100);  //裸指针cout << p << endl;//proc(p);  //语法错,int *p不能转换成shared_ptr<int>//shared_ptr<int> p2(p);//proc(p2);  //*p = 45;  //有两个shared_ptr,是没有问题的proc(shared_ptr<int>(p));  // 参数是个临时的shared_ptr,用一个裸指针显式的构造;*p = 45; // 出现潜在的不可预料的问题,因为p指向的内存已经被释放cout << p << endl;return 0;
}

把一个普通裸指针绑定到一个 shared_ptr 上之后,那么内存管理的责任就交给 shared_ptr了,这个时候你就不应该再用裸指针(内置指针)来访问 shared_ptr 所指向的内存了。

void proc(shared_ptr<int> value)
{return;
}int main()
{shared_ptr<int> p3(new int(100));proc(p3);*p3 = 45;return 0;
}

通过内存查看窗口显示地址在函数退出时已经释放,再次使用是不安全的。

绝对要记住不要用裸指针初始化多个shared_ptr

int main()
{int* p = new int(100);  //裸指针shared_ptr<int> p1(p);shared_ptr<int> p2(p);  //p1和p2无关联(p1和p2的每个强引用计数都为1了),会导致p1和p2所指向的内存释放两次,产生异常。shared_ptr<int> p1(new int);shared_ptr<int> p2(p1);  //这种写法就是OK的,p1和p2指向同一个内存地址并且两者是互通的(用的是同一个控制块);return 0;
}

<2>慎用get()返回的指针
返回智能指针指向的对象所对应的裸指针(有些函数接口可能只能使用裸指针)
get返回的指针不能delete,否则会异常。

int main()
{shared_ptr<int> myp(new int(100));int* pp = myp.get();delete pp;  //不可以删除,会导致异常return 0;
}

不能将其他智能指针绑到get返回的指针上!!!
不能将get返回的指针绑到其他智能指针上!!!

int main()
{shared_ptr<int> myp(new int(100));int* pp = myp.get();  //这个指针千万不能随意释放,否则myp就没办法正常管理该指针了{shared_ptr<int> myp2;myp2 = shared_ptr<int>(pp);shared_ptr<int> myp2(pp);  //现在myp和myp2引用计数都为1,但是一旦跳出这个程序块,离开上边这个myp2的作用域,导致myp指向的内存被释放了。}*myp = 65;  //该内存已经被释放,这样的赋值会导致不可预料的后果return 0;
}

结论:永远不要用get得到的一个指针初始化另一个智能指针或者给另外一个智能指针赋值。

<3>不要用类对象指针( this )作为 shared_ptr 返回,改用 enable_shared_form_this。

class CT
{
public:shared_ptr<CT> getself(){return shared_ptr<CT>(this);  // 用裸指针初始化了多个shared_ptr的感觉}
};int main()
{shared_ptr<CT> pct1(new CT);shared_ptr<CT> pct2 = pct1;  //这是两个强引用shared_ptr<CT> pct2 = pct1->getself();  //问题出现return 0;
}

用c++标准库里面的类模板:enable_shared_from_this。
现在在外面创建CT对象的智能指针以及通过CT 对象返回的 this 智能指针都是安全的
这个 enbale_shared_from_this 中有个弱指针 weak_ptr,这个弱指针能够监视 this。
在我们调用 shared_from_this() 这个方法时,这个方法内部实际上是调用这个 weak_ptr 的lock() 方法。
大家都知道 lock() 方法会让 shared_ptr 引用计数+1,同时返回这个 shared_ptr,这个就是工作原理。

<4>避免循环引用:能够导致内存泄漏 ???析构函数执行顺序是依照什么来的???

class CB;  //声明一下CBclass CA
{
public:shared_ptr<CB> m_pbs;~CA(){int test;test = 1;}
};class CB
{
public://shared_ptr<CA> m_pas;weak_ptr<CA> m_pas;  //把这里变成弱引用~CB(){int test;test = 1;}
};int main()
{shared_ptr<CA> pca(new CA);shared_ptr<CB> pcb(new CB);pca->m_pbs = pcb;  //等价于指向CB对象的有两个强引用pcb->m_pas = pca;  //1.等价于指向CA对象的有两个强引用  2.因为m_pas是弱引用,这里指向CA的对象只有一个强引用return 0;
}

离开作用域之后,pca引用计数从1变成0会释放 CA 对象(CA的析构函数被执行)。
CA 的析构函数被执行了(表示对象即将被释放),导致CA内的 m_pbs 引用计数减1;
也就是指向CB的对象引用计数减1,超出pcb作用域指向CB的引用计数也会减1,最终,会有一个时刻,指向CB对象的强引用计数会从1减少到0,导致CB对象被释放。CA先析构,CB后析构。

三:性能说明

<1>尺寸问题
shared_ptr的尺寸是裸指针的两倍, weak_ptr的尺寸也是裸指针的两倍。

int main()
{char* p;int ilenp = sizeof(p);  //4字节shared_ptr<string> p1;int ilensp = sizeof(p1);  //8字节,包含两个裸指针的return 0;
}

(a)第一个裸指针指向这个智能指针所指向的对象;
(b)第二个裸指针指向一个很大的数据结构(控制块),这个控制块里边有:
(b.1) 所指对象的强引用计数: shared_ptr
(b.2) 所指对象的弱引用计数: weak_ptr
(b.3) 其他数据,比如删除器指针,内存分配器等等
这个控制块是由第一个指向某个对象的shared_ptr来创建的

控制块创建时机
(a)make_shared:分配并初始化一个对象,返回指向此对象的 shared_ptr,所以,这个make_shared 它总是能够创建一个控制块
shared_ptr p2 = make_shared(100);

(b)用裸指针来创建一个 shared_ptr 对象时
int* pi = new int();
shared_ptr p1(pi);
shared_ptr p2(pi); // 不允许,否则会产生多个控制块,也就是多个引用计数(每个都是1),析构时析构多次,导致异常
shared_ptr p2(new int(100));

<2>移动语义
shared_ptr p1(new int(100));
shared_ptr p2(std::move(p1)); // 移动语义,移动构造一个新的智能指针对象p2,p1就不在指向该对象了(变为空),引用计数依旧是1

shared_ptr p3;
p3 = std::move(p2); //移动赋值,p2指向空,p3指向该对象,整个对象引用计数仍旧为1;

移动肯定比复制快,复制你要增加引用计数,移动不需要;
移动构造函数快过复制构造函数,移动赋值运算符快过拷贝赋值运算符

四:补充说明和使用建议

<1>掌握了绝大部分shared_ptr用法, 小部分没讲解,靠大家摸索
分配器解决内存分配问题
shared_ptr p((new int()), mydeleter(), mymallocator());

<2>谨慎使用,凡是老师没讲到过的用法,
new shared_ptr, memcpy() 没有提及的奇怪用法,不要轻易尝试

<3>优先使用make_shared(),不能定义自己的删除器 ???
shared_ptr ps(new string(“I Love China”)); // 分配两次内存
auto p2 = make_shared(“I Love China”); // 分配一次内存

*16.5 shared_ptr使用场景、陷阱、性能分析与使用建议相关推荐

  1. shared_ptr使用场景、陷阱、性能分析,使用建议

    1.std::shared_ptr使用场景 #include <iostream> #include <memory>using namespace std;shared_pt ...

  2. redis提高oracle性能,redis性能分析与优化建议

    首先,并不是说redis是内存应用就完全没性能问题,用的不好,还是会出现各种状况,例如RDB频繁,碎片太多等. 性能分析 info信息: 在redis-cli进入登录界面后,输入info all,或者 ...

  3. LIST函数JAVA特点_Java 集合系列 07 List总结(LinkedList, ArrayList等使用场景和性能分析)...

    java 集合系列目录: 第1部分 List概括 先回顾一下List的框架图 (01) List 是一个接口,它继承于Collection的接口.它代表着有序的队列. (02) AbstractLis ...

  4. Es底层查询原理、数据结构、及性能分析

      Elasticsearch是一个很火的分布式搜索系统,提供了非常强大而且易用的查询和分析能力,包括全文索引.模糊查询.多条件组合查询.地理位置查询等等,而且具有一定的分析聚合能力.因为其查询场景非 ...

  5. 云原生数据湖为什么要选择腾讯云大数据DLC,一份性能分析报告告诉你!

    摘要 日前,腾讯云大数据数据湖计算 DLC 与国内两家知名云厂商的数据湖产品进行了性能对比,其中腾讯云 DLC 在三款产品中SQL平均执行查询时间短,性能表现优.腾讯云大数据 DLC 在存算分离和大数 ...

  6. Tesla T4视频编码性能分析

    Tesla T4视频编码性能分析 从开普勒开始的所有 NVIDIA GPUs 都支持完全加速的硬件视频编码: GPUs 支持完全加速的硬件视频解码.最近发布的图灵硬件提供了张量核心和更好的机器学习性能 ...

  7. 独家揭秘!阿里大规模数据中心的性能分析

    阿里妹导读:数据中心已成为支撑大规模互联网服务的标准基础设施.随着数据中心的规模越来越大,数据中心里每一次软件(如 JVM)或硬件(如 CPU)的升级改造都会带来高昂的成本.合理的性能分析有助于数据中 ...

  8. NS2仿真:公交车移动周期模型及性能分析

    NS2仿真实验报告3 实验名称:公交车移动周期模型及性能分析 实验日期:2015年3月16日~2015年3月21日 实验报告日期:2015年3月22日 一.实验环境(网络平台,操作系统,网络拓扑图) ...

  9. 系统级性能分析工具perf的介绍与使用

    测试环境:Ubuntu16.04 + Kernel:4.4.0-31 apt-get install linux-source cd /usr/src/tools/perf make &&am ...

  10. Linux常用性能分析工具汇总

    文章目录 性能分析工具 top pstree mpstat vmstat pidstat perf proc tcpdump bcc工具箱 cachestat cachetop memleak fil ...

最新文章

  1. shell 编程整合
  2. 一个简单的DELPHI自定义事件的例子(转)
  3. 大厂python面试题_BAT大厂Python面试题精选,看完后离拿到offer只有一步之遥(含答案)...
  4. iOS 之 内存管理
  5. mysql 隔离级别 知乎_TiDB 事务隔离级别
  6. 大话数据结构13:二叉树 数组存储
  7. 洛谷P2085ssl1411OJ1370-最小函数值【堆,贪心】
  8. C++ 内存基本构件 placement new
  9. mysql 获取结果_【原创】7. MYSQL++中的查询结果获取(各种Result类型)
  10. 提升应用程序弹性:保障工作负载正常运行
  11. Liferay 7:Liferay内部博客地址
  12. TTL串口传输时,当收发线的长度和地线的长度不一样时,发生数据传输错误
  13. linux服务器创建FTP
  14. fl2440hello world模块驱动编写
  15. LeetCode刷题——80. 删除排序数组中的重复项 II
  16. Android LitePal
  17. 电脑内录软件哪个好,你一直都在如何录制电脑内部声音?
  18. .net反编译工具Reflector下载(转)
  19. Git cherry-pick 详解
  20. unity学习之遮挡剔除

热门文章

  1. wuauclt.exe进程和wuauclt病毒的查杀清理方法
  2. C#项目获取当前时间的农历时间
  3. 我的世界空岛生存服务器制作,《我的世界》空岛生存地图玩法 教你如何安全度过前期生存下去...
  4. 上海启用大数据捉拿套牌车 被套牌应立即报案
  5. 警告: Category is implementing a method which will also be implemented by its primary class
  6. 计算机病毒大多数具有自身复制的功能,《计算机基础》第五章练习题
  7. 初级、中级、高级程序员的区别在哪里?
  8. 安卓文件管理神器--X-plore
  9. iphone手机如何修改Apple ID密码
  10. 方程检验格式图片_解方程并检验(图片) x+3.5=3.5解方程