1.std::shared_ptr使用场景

#include <iostream>
#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> myfunc(int value) {shared_ptr<int> ptmp = create0(10);//return; // 离开作用域后,ptemp会被自动释放,它所指向的内存也会自动释放return ptmp; // 系统是根据ptmp这个局部变量来产生一个临时的shared_ptr对象往回返
}int main() {//一:std::shared_ptr使用场景myfunc(12);// 如果这块不用shared_ptr变量来接收myfunc返回的结果,那么从myfunc返回的shared_ptr// 就会被销毁,所指向的对象也会被销毁auto p11 = myfunc(12);// 我们用了一个变量来接myfunc的返回值,那么myfunc返回的shared_ptr// 就不会被销毁,它所指向的对象也不会被销毁return 0;
}

二、std::shared_ptr使用陷阱分析:一旦用错,也是致命的

2.1慎用裸指针

避免使用匿名的临时shared_ptr<T>对象(Effective C++中写道)

#include <iostream>
#include <memory>using namespace std;void proc(shared_ptr<int> value) {
}int main() {int*p = new int(100); //裸指针//proc(p); // 语法错, int*p 不能转换成shared_ptr<int>//error: could not convert ‘p’ from ‘int*’ to ‘std::shared_ptr<int>’
#if 0shared_ptr<int> p2(p);proc(p2);
#endifproc(shared_ptr<int>(p));  // 参数是个临时的shared_ptr,用一个裸指针显示的构造*p = 45; // 潜在的不可预料的问题;因为p指向的内存已经被释放了return 0;
}

2.2慎用get()返回的指针

返回只能指针指向的对象所对应的裸指针(有些函数接口可能只能使用裸指针)

get返回的指针不能delete,否则会异常

#include <iostream>
#include <memory>                                                                                   using namespace std;                                                                                int main() {
#if 0                                                                                               shared_ptr<int> myp(new int(100));                                                              int *p = myp.get();                                                                             delete p; // 不可以删除,会导致异常
#endif                                                                                              // 不能将其他智能指针绑到get返回的指针上                                                        shared_ptr<int> myp(new int(100));                                                              int *p = myp.get(); // 这个指针千万不能随意释放,否则myp就没办法正常管理该指针了                 {                                                                                               shared_ptr<int> myp2(p);// 错误,现在myp和myp2引用计数都为1,但一旦跳出这个程序块,p指针指向的内存被释放                 // shared_ptr<int> myp2;                                                                    // myp2 = shared_ptr<int>(myp); // 错误                                                     // shared_ptr<int> myp2(myp); // 正确                                                       }                                                                                               // 离开上边myp2的范围,导致myp指向的内存被释放了                                                 *myp = 65; //该内存已经被释放,这样复制会导致不可预料的后果                                     return 0;
} 

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

2.3不要把类对象指针(this)作为shared_ptr返回,改用enable_shared_from_this

#include <iostream>
#include <memory>                                                                                   using namespace std;                                                                                class CT: public enable_shared_from_this<CT> {
public:                                                                                             shared_ptr<CT> get_self() {                                                                     // return shared_ptr<CT>(this); // 用裸指针初始化多个shared_ptr的感觉                       return shared_from_this(); // 这个就是enable_shared_from_this类中的方法                     }
};                                                                                                  int main() {                                                                                        shared_ptr<CT> pct1(new CT);                                                                    // shared_ptr<CT> pct2 = pct1; // 这两个强饮用;                                                 shared_ptr<CT> pct2 = pct1->get_self();                                                         cout << pct2.use_count() << endl; // 2                                                               // get_self函数如果使用裸指针,会出现问题                                                        // 用到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,这就是工作原理               return 0;
} 

2.4避免循环引用:能够导致内存泄露

妖异的代码

#include <iostream>
#include <memory>using namespace std;class CB;
class CA {
public:shared_ptr<CB> m_pbs;~CA() {int test;test =1;cout << "~CA()" << endl;}
};class CB {
public://shared_ptr<CA> m_pas;weak_ptr<CA> m_pas; // 把这里变成弱引用~CB() {int test;test =1;cout << "~CB()" << endl;}
};int main() {
#if 0shared_ptr<CA> pca(new CA);shared_ptr<CB> pcb(new CB);pca->m_pbs = pcb;  // 等价于指向CB对象的有两个强引用pcb->m_pas = pca; // 妖异,等价于指向CA对象的有两个强引用
#endifshared_ptr<CA> pca(new CA);shared_ptr<CB> pcb(new CB);pca->m_pbs = pcb;  // 等价于指向CB对象的有两个强引用pcb->m_pas = pca; // 因为m_pas是弱引用,所以这里指向CA的对象只有一个强引用// 离开作用域之后,pca引用技术从1就变成0会释放CA对象(CA的析构函数被执行)// CA的析构函数被执行了(表示对象即将被释放),// 导致CA内的m_pbs引用技术会减1,也就是指向CB对象的引用技术-1// 超出pcb作用域时指向CB的计数也会-1,// 最终,会有一个时刻,指向CB对象的强引用计数从1较少到0,导致CB对象被释放 return 0;
}

三、性能说明

3.1尺寸问题

#include <iostream>
#include <memory>using namespace std;int main() {char *p;int lenp = sizeof(p); // 4字节cout << lenp << endl;shared_ptr<string> p1;int ilensp = sizeof(p1);cout << ilensp << endl; // 8字节,包含两个裸指针return 0;
}

shared_ptr的尺寸是裸指针的2倍;weak_ptr尺寸裸指针的2倍

a)第一个裸指针指向的是这个智能指针所指向的对象

b)第二个裸指针指向一个很大的数据结构(控制块),这个控制块里边有啥:

b.1)所指对象的强引用计数:shared_ptr

b.2)所指对象的弱引用计数:weak_ptr

b.3)其他数据,比如删除器指针,内存分配器

这个控制块是由第一个指向某个对象的shared_ptr创建的

控制块创建时机:

a)make_shared:分配并初始化一个对象,返回指向对象的shared_ptr,所以,这个make_shared它总是能够创建一个控制块
shared_ptr<int> p2 = make_shared<int>(100);
b)用裸指针来创建一个shared_ptr对象时
int*p = new int();
shared_ptr<int> p1(pi);
shared_ptr<int> p2(pi); // 不允许,否则会产生多个控制块,也就是多个引用计数(每个都是1);析构时析构多次,导致异常

3.2.移动语义

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

移动肯定比复制快;复制你要增加引用技术,移动不需要,

移动构造函数快过复制构造函数,移动赋值运算符快过拷贝赋值运算符

4.补充说明和使用建议

a)掌握了绝大部分shared_ptr用法;小部分没讲解,靠大家摸索

分配器,解决了内存分配问题

shared_ptr<int> p((new int), mydeleted(), myallocator<int>())

b)谨慎使用

new shared_ptr<int>, memcpy() 奇怪用法,大家不要轻易尝试

c)优先使用make_shared(),不能让自己定义自己的删除器

shared_ptr<string> ps1(new string("I Love China!")); // 分配两次
auto ps2 = make_shared_<string>("I love Chind!"); // 分配1次内存

5.多线程下shared_ptr

(1)一个 shared_ptr 对象实体可被多个线程同时读取;

(2)两个 shared_ptr 对象实体可以被两个线程同时写入,“析构”算写操作;

(3) 如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁。

https://blog.csdn.net/solstice/article/details/8547547

(4)多线程的情况下,shared_ptr的use_count成员技数是不准确的。业务场景:如果使用use_count是否等于1,判断是否有其他线程在使用。是不正确的。

常用使用包括

  • 与 ​0​ 比较。若 use_count 返回零,则智能指针为且不管理对象(无论被存储指针是否为空)。多线程环境下,这不隐含被管理对象的析构函数已完成。
  • 与 1 比较。若 use_count 返回 1 ,则无其他拥有者。(被弃用成员函数 unique() 为此使用情况提供。)多线程环境中,这不隐含对象可以安全修改,因为先前拥有者对被管理对象的访问可能未完成,而因为新的共享拥有者可以同时引入,例如用 std::weak_ptr::lock 。

https://zh.cppreference.com/w/cpp/atomic/memory_order

https://zh.cppreference.com/w/cpp/memory/shared_ptr/use_count

https://www.bookstack.cn/read/brpc-0.9.7-zh/90305fcdc498db34.md

多线程安全引用计数读写:https://github.com/apache/incubator-brpc/blob/master/src/butil/memory/scoped_ptr.h

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

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

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

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

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

  3. 【chrome devtools】前端性能分析之chrome devtools的使用 前端项目内存性能优化的建议 前端浏览器崩溃卡死 前端性能分析实战

    最近有个项目,静置一段时间,chrome内存一直上涨.就像是这样: 内存会慢慢悄悄的往上涨,最终可以到达2000多M,直至浏览器崩溃卡死.很明显,这应该是内存泄漏了. 但是只知道内存泄漏,并不知道究竟 ...

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

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

  5. golang(3)高质量编程与性能分析

    1. 编程原则 实际场景千变万化,各种语言各不相同,但是高质量编程遵循的原则是相通的. 简单性 消除"多余的复杂性",以简单清晰的逻辑写代码 不理解的代码无法修复改进 可读性 代码 ...

  6. Yolov4性能分析(下)

    Yolov4性能分析(下) 六. 权重更新 "darknet/src/detector.c"–train_detector()函数中: ....../* 开始训练网络 */floa ...

  7. 关于 Rocksdb 性能分析 需要知道的一些“小技巧“ -- perf_context的“内功” ,systemtap、perf、 ftrace的颜值

    文章目录 内部工具 包含头文件 接口使用 核心指标 Perf Context IOStats Context 外部工具 Systemtap 工具 Perf工具 Ftrace 工具 2020.8.20 ...

  8. MySQL:教你学会如何做性能分析与查询优化

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 作者:mikevictor cnblogs.com/mikevic ...

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

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

最新文章

  1. 架设win2003r2下配置好iis6+php+mysql_WIN2003+IIS6 PHP 5.3.8安装配置教程[图文]
  2. vue 手机键盘把底部按钮顶上去
  3. erlang精要(13)-基本语法(1)
  4. jzoj2679-跨时代【背包,dfs,状压】
  5. java ftp读取文件内容_java读取ftp中TXT文件的案例
  6. 2场直播丨MySQL 数据库最常见的 6 类故障的排除方法、2020数据技术嘉年华·金融峰会暨数据库大咖讲坛(第4期)...
  7. 导航类查询词的收集方法
  8. Alt+/ 快速提示快捷键修复及ecplise心得
  9. 2021-09-30光纤组会
  10. Django博客项目实战
  11. HTML5 常见问题 font标签设置字体未生效
  12. Skywalking vs Pinpoint
  13. 从一循环到150,并在每行打印一个值,另外在每个3的倍数行上打印出“foo“,在5的倍数行打印biz,在7的倍数行打印baz
  14. PC微信机器人之实战分析微信图片加密解密
  15. 主流室内定位技术分析
  16. 海报设计师必做3件事
  17. 配置 Eureka Server 集群
  18. linux sht11驱动,SHT11.h下载 温湿度传感器SHT11的驱动程序,提供了外界调用接口函数...
  19. USB 协议 (五) 枚举
  20. Java实验之设计一个动物声音“模拟器”,希望模拟器可以模拟许多动物的叫声

热门文章

  1. 在Apache上调试Asp.net 1.1/2.0代码
  2. oracle启动限制模式
  3. KPI总结模板:What
  4. Golang json 解析与生成
  5. netcore 实现一个简单的Grpc 服务端和客户端
  6. (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  7. C#实现实时监控文件目录下的变化
  8. java.lang.NoClassDefFoundError: org/aopalliance/aop/Advice
  9. KVM源代码阅读--内核版本3.17.4
  10. C# 线程手册 第四章 线程设计原则 对等线程模型