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.pswp.cn/news/382800.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/382800.shtml
英文地址,請注明出處:http://en.pswp.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…