shared_ptr解决了scoped_ptr管理单个对象的缺陷,且解决了防拷贝的问题。shared_ptr可以管理多个对象,并且实现了资源共享。

但是仍然存在一些问题,比如,我们熟悉的双向链表:

struct Node
{
Node(const int& value)
:_pNext(NULL)
,_pPre(NULL)
,_value(value)
{}
Node* _pNext;
Node* _pPre;
int _value;
};

这个双向链表对于shared_ptr会有什么影响呢?

1、shared_ptr的循环引用问题

先看如下代码:

#include<iostream>
using namespace std;
#include<boost/shared_ptr.hpp>
template<typename T>
class Node
{
public:Node(const T& value):_pNext(NULL),_pPre(NULL)_value(value){}shared_ptr<Node<T>> _pNext;shared_ptr<Node<T>> _pPre;T _value;
};void FunTest()
{shared_ptr<Node<int>> sp1(new Node<int>(1));shared_ptr<Node<int>> sp2(new Node<int>(2));cout<<sp1.use_count()<<endl;cout<<sp2.use_count()<<endl;sp1->_pNext = sp2;sp2->_pPre = sp1;cout<<sp1.use_count()<<endl;cout<<sp2.use_count()<<endl;
}

运行结果:

这就是shared_ptr实现的双向链表的模型,在此处引起了循环引用的问题。

如图:

当分别创建完sp1,sp2后,它们各自的use_count为1;当再次执行

 sp1->_pNext = sp2;sp2->_pPre = sp1;

这两句后,sp1,sp2的use_count分别加为2;

此时,析构对象时use_count会减为1;但不等于0,所以它不会释放,这就导致了循环引用问题。

那么如何解决循环引用问题呢?我们又引出了另外一个智能指针:weak_ptr(它是一个弱指针,用来和shared_ptr搭配使用的)

2、解决循环引用问题

#include<iostream>
using namespace std;
#include<boost/shared_ptr.hpp>
template<typename T>
class Node
{
public:Node(const T& value):_value(value){}T _value;weak_ptr<Node<T>> _pNext;weak_ptr<Node<T>> _pPre;
};void FunTest()
{shared_ptr<Node<int>> sp1(new Node<int>(1));shared_ptr<Node<int>> sp2(new Node<int>(2));cout<<sp1.use_count()<<endl;cout<<sp2.use_count()<<endl;sp1->_pNext = sp2;sp2->_pPre = sp1;cout<<sp1.use_count()<<endl;cout<<sp2.use_count()<<endl;
}

运行结果:

其实,在shared_ptr和weak_ptr的引用计数的基类中,有两个计数:一个是_Uses,一个是_Weaks;

shared_ptr:当指向一片区域时,引用计数会使用_Uses来++;

weak_ptr:当指向一片区域时,引用计数会使用_Weaks来++;

最终看的还是use_count,使用weak_ptr时use_count仍为1;所以析构时可以成功释放。

3、定置删除器

原理:对于像文件类型的指针,用shared_ptr释放时,无法释放,因为在底层没有对文件指针的直接释放,所以得自己手动将其close掉。

void FunTest()
{
FILE* file = fopen("1.txt","r");
shared_ptr<FILE> sp(file);
}

这时,就要使用我们的定置删除器:(此处用了STL的六大组件之一-----仿函数)

//成功的关闭文件:

#include<iostream>
using namespace std;
#include<boost/shared_ptr.hpp>struct FClose
{void operator()(FILE *file){fclose(file);cout<<"fclose()"<<endl;}
};void FunTest()
{FILE* file = fopen("1.txt","w");shared_ptr<FILE> sp(file,FClose());
}

//类似的,对于我们malloc出来的空间,需要free掉时,同样也可以用仿函数的形式:

struct Free
{void operator()(void *ptr){free(ptr);cout<<"free()"<<endl;}
};
void FunTest()
{int *p = (int *)malloc(sizeof(int));shared_ptr<int> sp(p,Free());
}

4、冒泡排序的升级版(仿函数的形式)

有时候,当面试官让你写一个冒泡排序的时候,你不知道面试官到底让你写的是升序还是降序,此时就比较尴尬了哈,你可以问一下面试官也是可以的,当然还有一种更巧妙的方法就是:你可以用仿函数的方式,把两种方式都实现了,需要哪种用哪种即可。

#include<iostream>
using namespace std;
template<typename T>
class Greater
{
public:bool operator()(const T&left,const T& right){return left>right;}
};
template<typename T>
class Less
{
public:bool operator()(const T&left,const T& right){return left<right;}
};template<typename T,typename Fun>
void BubbleSort(T arr[],size_t size)
{for(size_t i = 0; i < size-1; i++){for(size_t j = 0; j < size-i-1;++j){if(Fun()(arr[j],arr[j+1])){T tmp = arr[j];arr[j] = arr[j+1];arr[j+1] = tmp;}}}}
void FunTest()
{int arr[] = {2,5,4,1,6,9,8,7};BubbleSort<int,Greater<int>>(arr,sizeof(arr)/sizeof(arr[0]));BubbleSort<int,Less<int>>(arr,sizeof(arr)/sizeof(arr[0]));
}
int main()
{FunTest();return 0;
}

如果可以写成这种程度,肯定会使面试官眼前一亮。哈哈

c++【深度剖析shared_ptr】相关推荐

  1. libevent源码深度剖析

    原文地址:http://blog.csdn.net/sparkliang/article/details/4957667 libevent源码深度剖析一 --序幕 张亮 1 前言 Libevent是一 ...

  2. libevent源码深度剖析十一

    libevent源码深度剖析十一 --时间管理 张亮 为了支持定时器,Libevent必须和系统时间打交道,这一部分的内容也比较简单,主要涉及到时间的加减辅助函数.时间缓存.时间校正和定时器堆的时间值 ...

  3. 《AngularJS深度剖析与最佳实践》一第1章 从实战开始

    本节书摘来自华章出版社<AngularJS深度剖析与最佳实践>一书中的第1章,作者 雪狼 破狼 彭洪伟,更多章节内容可以访问云栖社区"华章计算机"公众号查看 第1章 从 ...

  4. 深度剖析:Redis分布式锁到底安全吗?看完这篇文章彻底懂了!

    ‍‍‍‍‍‍‍‍‍‍‍‍阅读本文大约需要 20 分钟. 大家好,我是 Kaito. 这篇文章我想和你聊一聊,关于 Redis 分布式锁的「安全性」问题. Redis 分布式锁的话题,很多文章已经写烂了 ...

  5. [Android] Toast问题深度剖析(二)

    欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 作者: QQ音乐技术团队 题记 Toast 作为 Android 系统中最常用的类之一,由于其方便的api设计和简洁的交互体验,被我们所广泛采用 ...

  6. Mysql binlog应用场景与原理深度剖析

    1 基于binlog的主从复制 Mysql 5.0以后,支持通过binary log(二进制日志)以支持主从复制.复制允许将来自一个MySQL数据库服务器(master) 的数据复制到一个或多个其他M ...

  7. SQL Server性能调优之执行计划深度剖析 第二节 执行计划第一次实践

    SQL Server性能调优之执行计划深度剖析 第二节 执行计划第一次实践 前言:自从上一篇文章发出之后,收到了很朋友的关注.很多朋友要求多多实践,而不是纯粹的理论.确实,从打算出这个系列开始,我就本 ...

  8. 嵌入式网络那些事LwIP协议深度剖析与实战演练pdf

    下载地址:网盘下载 <嵌入式网络那些事:LwIP协议深度剖析与实战演练>面向网络TCP/IP协议初学者以及大量嵌入式网络开发人员,从当下流行的嵌入式网络协议栈LwIP的源代码入手,详细讲解 ...

  9. 深度剖析Zabbix Web scenarios数据表结构

    深度剖析Zabbix Web scenarios数据表结构 前言 因开发需求,需要解析Zabbix web监控数据表结构:因为网上关于Zabbix数据表结构解析的比较少,所以心血来潮写了一篇作为记录. ...

最新文章

  1. 计算机网络体系结构作业题整理-第十章答案
  2. 《能源专业的展望及其未来择业方向的分析》
  3. ROS探索总结(四)——简单的机器人仿真
  4. 1 1 2 3 5 8 java_1 1 2 3 5 8 13 21 34规律:一个数据等于前两个数之和.用java做,输入一个数据n,计算斐波那契数列(Fibonacci)的第n个值....
  5. camera驱动电源配置_基于AD7656-1和ADuC7026评估电源时序控制影响
  6. Qt Quick编程(1)——QML的核心部分ECMAScript
  7. WordPress超级基本教程(转)
  8. 网络安全传输(读书笔记)
  9. Kali [Nmap]端口扫描工具
  10. JavaScript设计模式——访问者模式
  11. TSV文件、CSV文件
  12. 通过AI,领略皮影戏艺术 | MixLab人工智能
  13. HR面试问题-离职原因
  14. 笔记:戴蒙德模型——参数变化的影响
  15. 如何登录数据库和运行
  16. 数据库学习:数据模型
  17. SRE重案调查组 第二集 | 挖掘应用处理变慢的“真相”
  18. React高阶组件探究
  19. 文学阅读---菜根谭(1)
  20. JDBC编程和DAO设计模式

热门文章

  1. leetcode 134. 加油站(Gas Station)
  2. Gitlab 项目上传
  3. flask基础之jinja2模板-语法定义
  4. SEO 百度后台主动推送链接
  5. jquery GET POST
  6. java基础知识系列---垃圾收集
  7. Forbidden You don't have permission to access / on this server PHP
  8. predict函数 R_学习|R语言做机器学习的常用函数总结
  9. java仿qq空间音乐播放_完美实现仿QQ空间评论回复特效
  10. python含多个附件的邮件_Python发送带有多个图像附件的电子邮件