詳解C++?智能指針的刪除器
為什么要設(shè)置刪除器
C++11 加入STL的 shared_ptr 和 unique_ptr,已經(jīng)是我們編碼的??土恕S玫亩嘧匀痪蜁?huì)了解到它們的刪除器,比如很多C語(yǔ)言庫(kù)(GDAL, GLFW, libcurl等等)創(chuàng)建的指針不能簡(jiǎn)單的使用 delete 釋放,當(dāng)我們想使用智能指針管理這些庫(kù)創(chuàng)建的資源時(shí),必須設(shè)置刪除器:
//使用重載了operator()的類作為刪除器 struct CurlCleaner { void operator()(CURL *ptr) const { curl_easy_cleanup(ptr); } }; std::unique_ptr<CURL, CurlCleaner> curlu(curl_easy_init(), CurlCleaner{});//第二個(gè)參數(shù)可省略,因?yàn)镃urlCleaner可默認(rèn)構(gòu)造 std::shared_ptr<CURL> curls(curl_easy_init(), CurlCleaner{}); //使用函數(shù)指針作為刪除器 void GLFWClean(GLFWwindow *wnd) { glfwDestroyWindow(wnd); } std::unique_ptr<GLFWwindow, decltype(&GLFWClean)> glfwu(glfwCreateWindow(/*省略*/), GLFWClean);//第二個(gè)參數(shù)必須傳入實(shí)際調(diào)用的函數(shù)地址 std::shared_ptr<GLFWwindow> glfws(glfwCreateWindow(/*省略*/), GLFWClean); //上述兩個(gè)構(gòu)造函數(shù)中的第二個(gè)參數(shù)都進(jìn)行了函數(shù)名到函數(shù)指針的隱式轉(zhuǎn)換 //使用lambda作為刪除器 auto GDALClean=[](GDALDataset *dataset){ GDALClose(dataset); }; std::unique_ptr<GDALDataset, decltype(GDALClean)> gdalu(GDALOpen(/*省略*/), GDALClean);//lambda無(wú)法默認(rèn)構(gòu)造,必須傳入一個(gè)實(shí)例 std::shared_ptr<GDALDataset> gdals(GDALOpen(/*省略*/), GDALClean);
上面是三種最常使用的自定義刪除器形式,也可以利用 std::function 的強(qiáng)大適配能力來(lái)包裝可調(diào)用對(duì)象作為刪除器,此處不展開(kāi)。
標(biāo)準(zhǔn)庫(kù)提供的默認(rèn)刪除器
內(nèi)置類型和析構(gòu)函數(shù)為 public 的類類型,無(wú)需指定刪除器,智能指針會(huì)在引用計(jì)數(shù)歸零時(shí)自動(dòng)調(diào)用 delete 對(duì)管理的指針進(jìn)行釋放,使得語(yǔ)法相對(duì)簡(jiǎn)潔:
std::unique_ptr<int> pi(new int(42)); std::shared_ptr<float> pf(new float(0.0f)); std::unique_ptr<std::vector<int>> pveci(new std::vector<int>()); std::unique_ptr<std::list<int>> plsti(new std::list<int>());
很長(zhǎng)一段時(shí)間內(nèi),我以為智能指針只有 delete 一個(gè)默認(rèn)的刪除器,所以每次在管理 new[] 得到的指針時(shí),都會(huì)為它編寫(xiě)調(diào)用 delete[] 的刪除器,直到翻看智能指針的源碼,發(fā)現(xiàn)它們的默認(rèn)刪除器其實(shí)有一個(gè)針對(duì)數(shù)組形式指針的特化版本:
template<class _Tp> struct default_delete//默認(rèn)刪除器主模板 { ... void operator()(_Tp *_Ptr) const noexcept { ... delete _Ptr;//使用delete釋放指針 } ... } template<class _Tp> struct default_delete<_Tp[]>//針對(duì)數(shù)組形式的特化版本 { ... void operator()(_Tp *_Ptr) const noexcept { ... delete[] _Ptr;//使用delete[]釋放指針 } ... } //unique_ptr template<class _Tp, class _Dp = default_delete<_Tp>/*默認(rèn)刪除器*/> class unique_ptr{...}; //shared_ptr template <class, class _Yp>//輔助類主模板,普通指針應(yīng)用該版本 struct __shared_ptr_default_delete : default_delete<_Yp> {}; template <class _Yp, class _Un, size_t _Sz>//數(shù)組形式特化,匹配固定長(zhǎng)度的數(shù)組形式,如std::shared_ptr<int[10]> struct __shared_ptr_default_delete<_Yp[_Sz], _Un> : default_delete<_Yp[]> {}; template <class _Yp, class _Un>//數(shù)組形式特化,匹配不定長(zhǎng)度的數(shù)組形式,如std::shared_ptr<int[]> struct __shared_ptr_default_delete<_Yp[], _Un> : default_delete<_Yp[]> {}; template<class _Tp> class shared_ptr { ... template <class _Yp,/*檢查_(kāi)Yp指針是否可轉(zhuǎn)換為_(kāi)Tp指針(比如子類指針到基類指針)、_Yp類型是否可應(yīng)用delete與delete[]操作*/> explicit shared_ptr(_Yp* __p) : __ptr_(__p) { ... typedef __shared_ptr_pointer<_Yp*, __shared_ptr_default_delete<_Tp, _Yp>/*根據(jù)_Yp類型選擇合適的默認(rèn)刪除器*/, _AllocT> _CntrlBlk; __cntrl_ = new _CntrlBlk(__p, __shared_ptr_default_delete<_Tp, _Yp>(), _AllocT()); ... } ... }; //用戶代碼 std::unique_ptr<int[]> piu(new int[10]);//匹配int[]版本,刪除器編譯為使用delete[]釋放指針 std::shared_ptr<int[]> pis(new int[10]);//構(gòu)造函數(shù)內(nèi)選擇使用delete[]釋放指針的刪除器
以上代碼節(jié)選自 llvm-mingw 的標(biāo)準(zhǔn)庫(kù),查看了一下手頭上的幾個(gè)版本的標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn),發(fā)現(xiàn) unique_ptr 的實(shí)現(xiàn)大致類似。值得一提的是,unique_ptr 自身也有針對(duì)數(shù)組形式的特化版本 unique_ptr<_Tp[]>,由于知曉管理的是數(shù)組形式的指針,這個(gè)特化版本不提供 operator-> 訪問(wèn)符號(hào),取而代之的是 operator[] 來(lái)訪問(wèn)數(shù)組數(shù)據(jù)。
llvm-mingw shared_ptr 默認(rèn)刪除器的選擇是通過(guò)輔助模板類 __shared_ptr_default_delete 的特化來(lái)實(shí)現(xiàn)的;MSVC 版本中 shared_ptr 的構(gòu)造函數(shù)則直接使用 if consexpr(雖然是 C++17 開(kāi)始支持,但是發(fā)現(xiàn) C++14 版本的代碼中已經(jīng)使用)判斷實(shí)例化指針類型是否為數(shù)組形式選擇相應(yīng)刪除器;GCC 的 shared_ptr 邏輯相對(duì)復(fù)雜一些,其 shared_ptr 繼承自 __shared_ptr,而 __shared_ptr 有一個(gè)類型為 __shared_count 的成員 _M_refcount, 該類有一系列重載的構(gòu)造函數(shù),其中幾個(gè)是:
struct __sp_array_delete { template<typename _Yp> void operator()(_Yp *__p) const { delete[] __p; } }; r1: template<typename _Ptr>/*默認(rèn)使用delete版本的刪除器,省略實(shí)現(xiàn)*/ explicit __shared_count(_Ptr __p) r2: template<typename _Ptr>/*委托給r1*/ __shared_count(_Ptr __P, false_type) : __shared_count(__p){} r3: template<typename _Ptr, typename _Deleter, typename _Alloc, typename = typename __not_alloc_shared_tag<_Deleter>::type> __shared_count(_Ptr __p, _Deleter __d, _Alloc __a)/*可指定刪除器、內(nèi)存分配器的版本*/ r4: template<typename _Ptr>/*委托給r3*/ __shared_count(_Ptr __p, true_type) : __shared_count(__p, __sp_array_delete{}, allocator<void>()){} //__shared_ptr的接受一個(gè)指針參數(shù)的構(gòu)造函數(shù) template<typename _Yp, /*檢查_(kāi)Yp *是否和轉(zhuǎn)換為類的實(shí)例化指針類型*/> explicit __shared_ptr(_Yp *__p): _M_ptr(__p), _M_refcount(__p, typename is_array<_Tp>::type()){...}
通過(guò)代碼我們大致可以推測(cè),__shared_count 這個(gè)類是用來(lái)管理引用計(jì)數(shù)和刪除器的類。
可以看到,如果 __shared_ptr 構(gòu)造函數(shù)接受的指針類型為普通指針,會(huì)調(diào)用 __shared_count(__p, false_type) 將 _M_refcount 構(gòu)造為使用 delete 釋放指針的版本;而當(dāng)它接受的指針類型為數(shù)組形式指針時(shí),__shared_count(__p, true_type) 則會(huì)被調(diào)用,構(gòu)造的 _M_refcount 存儲(chǔ)的刪除器是 __sp_array_delete 類型,這個(gè)類型使用 delete[] 釋放指針。
總結(jié)
1.銷毀前需要額外資源釋放操作的類型,使用智能指針管理時(shí)必須設(shè)置自定義刪除器
2.標(biāo)準(zhǔn)庫(kù)為智能指針提供了兩個(gè)默認(rèn)版本的刪除器,可簡(jiǎn)化智能指針的代碼編寫(xiě)
到此這篇關(guān)于C++ 智能指針的刪除器的文章就介紹到這了,更多相關(guān)C++ 智能指針內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)個(gè)人財(cái)務(wù)管理軟件
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)個(gè)人財(cái)務(wù)管理軟件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05VC基于ADO技術(shù)訪問(wèn)數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了VC基于ADO技術(shù)訪問(wèn)數(shù)據(jù)庫(kù)的方法,較為詳細(xì)的分析了VC使用ADO操作數(shù)據(jù)庫(kù)的相關(guān)實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10Qt Creator使用教程的簡(jiǎn)單說(shuō)明
如今 Qt Creator 功能十分強(qiáng)大了,包含項(xiàng)目模板生成、代碼編輯、UI 設(shè)計(jì)、QML 界面編輯、調(diào)試程序、上下文幫助等豐富功能,本文就詳細(xì)的介紹一下如何使用2021-08-08C++使用ffmpeg實(shí)現(xiàn)rtsp取流的代碼
這篇文章主要介紹了C++使用ffmpeg實(shí)現(xiàn)rtsp取流,文章介紹了ffmepg采用rtsp取流流程圖,CMakeLists.txt編寫(xiě)方法,通過(guò)示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單掃雷小程序
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單掃雷小程序,一款大眾類的益智小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10