c++【深度剖析shared_ptr】

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;
}

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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/382800.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

centos重新安装yum

1.备份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 2.下载新的CentOS-Base.repo 到/etc/yum.repos.d/ wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo 3. yum makecache GDB的安装 yum…

Electron 渲染进程,如何解决require is not defined的问题

mainWindow new BrowserWindow({webPreferences: {nodeIntegration: true}}) // nodeIntegration: true 加上这一句 就可以了 5.0以后默认是false

c++详解【new和delete】

说起new和delete&#xff0c;了解过c的人应该都知道吧&#xff0c;它是用来分配内存和释放内存的两个操作符。与c语言中的malloc和free类似。 c语言中使用malloc/calloc/realloc/free进行动态内存分配&#xff0c;malloc/calloc/realloc用来在堆上分配空间&#xff0c;free将申…

vim 的配置文件 #vim ~/.vimrc

set hlsearch set backspace2 set nu set showmode set ruler set autoindent syntax on set smartindent set tabstop4 set shiftwidth4 set expandtab imap { {}iV

关于tornado的异步耗时操作假设

tornado 如果遇到耗时的操作&#xff0c;可不可以这样 把耗时操作放在一个由 python进程池维护的 pool中&#xff0c; 用 webapi封装起来&#xff0c; 然后tornado 接收客户端请求后&#xff0c;遇到耗时操作就 与访问另一个webapi &#xff0c; webapi去调用进程池 这种模型不…

Stack/Queue与Vector/List的联系

Vector:(顺序表【数组存储】) 1.当申请的空间不足的时候&#xff0c;需要再次开辟一块更大的空间&#xff0c;并把值拷过去。 2.对于尾删和尾插是比较方便的&#xff0c;只需要改动最后一个元素即可。不会改动原有的空间。适用于多次重复的对尾部插删。 3.顺序存储&#xff…

利用SetConsoleTextAttribute函数设置控制台颜色

原文出处&#xff1a; https://blog.csdn.net/odaynot/article/details/7722240 混合颜色 #include <windows.h> #include <iostream> using namespace std;int main() {HANDLE hOut;hOut GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleTextAttribute(hOut,FOREG…

用栈实现后缀表达式求解问题

一、问题概述&#xff1a; 人们经常书写的数学表达式属于中缀表达式&#xff0c;今天要解决的是&#xff0c;后缀表达式的求解问题。 如下图分别为举例的中缀表达式和后缀表达式&#xff1a; 二、解决思路 我们用栈存储后缀表达式中的数据部分&#xff0c;当遇到操作符时就取…

SetConsoleCursorPosition光标的位置控制

SetConsoleCursorPosition是一个计算机函数&#xff0c;如果用户定义了 COORD pos&#xff0c;那么pos其实是一个结构体变量&#xff0c;其中X和Y是它的成员&#xff0c; 通过修改pos.X和pos.Y的值就可以实现光标的位置控制。 复制粘贴运行一下&#xff0c;你就明白代码什么意…

用栈和递归求解迷宫问题

一、问题概述 小时候&#xff0c;我们都玩过走迷宫的游戏吧。看一下这个图例&#xff1a; 遇到这种问题时&#xff0c;我们第一反应都会先找到迷宫的入口点&#xff0c;然后对上下左右四个方向进行寻迹&#xff0c; 检测当前位置是否是通路&#xff0c;是否可以通过&#xff0…

exit(0) return区别

1. return是返回函数调用&#xff0c;如果返回的是main函数&#xff0c;则为退出程序。 exit是在调用处强行退出程序&#xff0c;运行一次程序就结束&#xff0c; 无论写在那里&#xff0c;都是程序推出&#xff0c;括号里的数字0,1,-1会被写入环境变量ERRORLEVEL&#xff0c…

electron 5.0.3版本 改动的地方

BrowserWindow.getFocusedWindow 1. BrowserWindow.getFocusedWindow getFocusedWindow 已经不是一个方法了&#xff0c; 这个简单的问题解决了半天&#xff0c;因为我看文档上 还是当一个方法来调用&#xff0c; 文档没有正确更新&#xff0c;实际上已经变成了一个属性&#…

【c语言】棋盘游戏--三子棋

一、问题概述 大家都玩过棋盘游戏吧&#xff0c;像五子棋一样&#xff0c;玩家或者是电脑一人下一次&#xff0c;当玩家或者是电脑的某一方先将各自的五个棋子下成一条线时&#xff0c;谁就赢&#xff0c;棋盘游戏就会结束。 当然&#xff0c;我今天要介绍的是三子棋&#xff…

【转】浅析task_struct结构体

https://blog.csdn.net/peiyao456/article/details/54407343

electron 主进程与渲染进程 渲染进程与渲染进程 之间的通信

主进程与渲染进程之间的通信 这是渲染进程 // 渲染进程执行主进程里面的方法&#xff0c;主进程给渲染进程反馈处理结果 。 var sendreplayDomdocument.querySelector(#sendreplay); sendreplayDom.onclickfunction(){// alert(1213)//渲染进程给主进程广播数据ipcRenderer.se…

centos升级之gcc 升级 gcc-7.3.0安装

更新于&#xff1a;2018_7_28 安装时间非常非常久&#xff0c;我最快一次40分钟&#xff0c;最长一次两个小时 cd / wget ftp.gnu.org/gnu/gcc/gcc-7.3.0/gcc-7.3.0.tar.gz tar -zxvf gcc-7.3.0.tar.gz cd gcc-7.3.0 ./contrib/download_prerequisites mkdir build cd …

[数据结构]用插入排序和选择排序的思想实现优先级队列

一、问题概述 优先级队列的定义&#xff1a; 优先级队列不同于普通的队列&#xff0c;普通的队列具有先进先出的原则&#xff0c;而优先级队列是选择优先级最高的先出队。那么&#xff0c;如何模拟实现优先级队列呢&#xff1f;在这里&#xff0c;我们将较大的值作为优先级较高…

node.js https 模块设置请求头等信息

// https://www.iqiyi.com/v_19rs789v28.html var fs require(fs); var https require(https); var option{rejectUnauthorized: false,hostname:www.iqiyi.com,path:/,headers:{Accept:*/*,Accept-Encoding:utf-8, //这里设置返回的编码方式 设置其他的会是乱码Accept-Lang…

centos升级之vim vim8.0安装

YCM安装攻略&#xff1a;https://blog.csdn.net/csdn_kou/article/details/81213935 卸载旧的vim yum remove vim* -y 一、源码编译安装vim8.0 配置epel源 yum install epel-release 安装python3,以及vim8.0编译环境 yum install -y gcc python34 python34-devel ncurses…