C++之智能指針初步及棄用auto_ptr的原因分析
RAII
使用局部對(duì)象來(lái)管理資源的技術(shù)
RAII的原理
RAII的四個(gè)步驟
裸指針存在的問(wèn)題
delete后的指針變量就變成了一個(gè)失效指針(也叫作懸空指針)。
對(duì)于下面的代碼:
void Destroy(Object *op) { delete op; delete[] op; } Object *op = new Object(10); Object *arop = new Object[10]; Destroy(op); Destroy(arop);
因此:
智能指針
智能指針的引入
智能指針是比原始指針更加智能的類(lèi),解決懸空指針多次刪除被指向?qū)ο?,以及資源泄漏問(wèn)題,通常用來(lái)確保指針的壽命和其指向?qū)ο蟮膲勖恢隆?/p>
智能指針雖然很智能,很容易被誤用,智能也是有代價(jià)的。
四種智能指針
auto_ptr
unqiue_ptr
(唯一性智能指針)shared_ptr
(共享性智能指針)weak_ptr
(管理弱引用)
其中后三個(gè)是C11支持,并且第一個(gè)已經(jīng)被C11棄用。
C98中的auto_ptr
所做的事情,就是動(dòng)態(tài)分配對(duì)象以及當(dāng)對(duì)象不再需要時(shí)自動(dòng)執(zhí)行清理。
下面我們首先來(lái)了解一下為什么要將auto_ptr移除的原因:
因?yàn)樵擃?lèi)型的智能指針意義不明確,使用淺拷貝方式時(shí),兩個(gè)對(duì)象擁有同一塊資源:我們模仿源碼的邏輯
了解一下:比如下面的代碼:
class Object { int value; public: Object(int x = 0):value(x){cout<<"Create Object:"<<this<<endl;} ~Object(){cout<<"Destroy Object:"<<this<<endl;} int & Value(){return value;} const int& Value() const{return value;} }; template<class _Ty> class my_auto_ptr { private: bool _Owns;//所有權(quán) _Ty* _Ptr; public: my_auto_ptr(_Ty* p = NULL):_Owns(p != NULL),_Ptr(p){} ~my_auto_ptr() { if(_Owns) { delete _Ptr; } _Owns = false; _Ptr = NULL; } _Ty* get() const { return _Ptr; } _Ty* operator->()const { return get(); } _Ty & operator*() { return *get(); } void reset(_Ty* p = NULL) { if(_Owns) { delete _Ptr; } _Ptr = p; } _Ty * release()const//編譯要通過(guò),要么異變,要么強(qiáng)轉(zhuǎn)成普通指針 { _Ty* tmp = NULL; if(_Owns) { ((my_auto_ptr*)this)->_Owns = false; tmp = _Ptr; ((my_auto_ptr*)this)->_Ptr = NULL; } return tmp; } my_auto_ptr(const my_auto_ptr & op):_Owns(op._Owns) { if(_Owns) { _Ptr = op._Ptr; } } }; void fun() { my_auto_ptr<Object> pobj(new Object(10));//pobj是my_auto_ptr類(lèi)型 cout<<pobj->Value()<<endl; cout<<(*pobj).Value()<<endl;//(*pobj)是Object的堆區(qū)對(duì)象。*(pobj._Ptr).Value() } int main() { my_auto_ptr<Object> pobja(new Object(10)); my_auto_ptr<Object> pobjb(pobja); }
相關(guān)函數(shù)解釋?zhuān)?/p>
此時(shí)程序必然會(huì)導(dǎo)致程序崩潰引發(fā)異常,主函數(shù)結(jié)束時(shí)對(duì)同一部分資源釋放了兩次,堆內(nèi)存被釋放兩次
那么我們可能會(huì)考慮,將資源轉(zhuǎn)移,即修改拷貝構(gòu)造如下:利用是釋放函數(shù)
my_auto_ptr(const my_auto_ptr & op):_Owns(op._Owns),_Ptr(op.release()) {}
看似好像解決了上面的問(wèn)題,實(shí)則存在隱患
繼續(xù)來(lái)看:下面的代碼存在什么問(wèn)題呢?
void fun(my_auto_ptr<Object> apx) { int x = apx->Value(); cout<<x<<endl; } int main() { my_auto_ptr<Object> pobja(new Object(10)); fun(pobja); int a = pobja->Value(); cout<<a<<endl; }
上述代碼的執(zhí)行邏輯如下:
pobja
有兩個(gè)域擁有權(quán)域和指針域,拿pobja初始化形參apx時(shí),會(huì)調(diào)動(dòng)拷貝構(gòu)造函數(shù)apx
將自己的擁有權(quán)域設(shè)為1,調(diào)動(dòng)release函數(shù),銷(xiāo)毀了pobja對(duì)象的資源后,返回堆區(qū)對(duì)象的地址,apx接收后將自身的指針域指向原先pobja所指向的堆區(qū)對(duì)象fun
函數(shù)結(jié)束,apx局部對(duì)象就會(huì)被析構(gòu),此時(shí)再打印a,對(duì)象其實(shí)已經(jīng)不存在了并且自身早已失去了pobja的擁有權(quán)。
綜上,此時(shí)智能指針的拷貝構(gòu)造函數(shù)的兩種寫(xiě)法:
my_auto_ptr(const my_auto_ptr & op):_Owns(op._Owns) { if(_Owns) { _Ptr = op._Ptr; } } my_auto_ptr(const my_auto_ptr & op):_Owns(op._Owns),_Ptr(op.release()) {}
- 第一種存在的問(wèn)題:Object的資源會(huì)被兩個(gè)釋放兩次
- 第二種存在的問(wèn)題:解決了第一種問(wèn)題,但是不能解決類(lèi)似于實(shí)參對(duì)象初始化形參時(shí),實(shí)參之前自身的資源丟失的問(wèn)題,找不著了,因?yàn)檫@種情況太過(guò)于隱蔽,容易出錯(cuò),所以auto_ptr作為函數(shù)參數(shù)傳遞時(shí)一定要避免的。或許你想到加上引用解決上面的問(wèn)題,但是仔細(xì)思考后發(fā)現(xiàn),我們并不知道函數(shù)對(duì)傳入的傳入的auto_ptr做了什么,如果當(dāng)中的某些操作使其失去了對(duì)對(duì)象的所有權(quán),那么這還可能會(huì)導(dǎo)致致命的執(zhí)行期錯(cuò)誤。獲取再加上const 才是個(gè)不錯(cuò)的選擇。
因此,C11標(biāo)準(zhǔn)之前的auto_ptr
這個(gè)智能指針不被廣泛使用的原因就是:在某些應(yīng)用場(chǎng)景下,拷貝構(gòu)造函數(shù)的意義不明確,同理賦值語(yǔ)句也是這個(gè)道理,意義同樣不明確,因?yàn)镃11標(biāo)準(zhǔn)之前并不存在移動(dòng)賦值和移動(dòng)構(gòu)造的概念,還有就是之前談到的一個(gè)對(duì)象和一組對(duì)象的問(wèn)題,對(duì)于自定義類(lèi)型而言,auto_ptr
的析構(gòu)函數(shù)僅能夠析構(gòu)一個(gè)對(duì)象,不能夠處理一組對(duì)象的情況,這些都是尚未解決的問(wèn)題。
于是在C11中棄用,C17標(biāo)準(zhǔn)中直接移除。
歷史淵源:
在STL庫(kù)之前,有一個(gè)功能更加強(qiáng)大的boost庫(kù),STL為了與其抗衡,應(yīng)急制造了STL,但制作的不夠完善,由此因?yàn)镾TL未解決auto_ptr
的問(wèn)題,因此STl內(nèi)的容器vector和list都不想和auto_ptr建立聯(lián)系。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
全排列算法的非遞歸實(shí)現(xiàn)與遞歸實(shí)現(xiàn)的方法(C++)
本篇文章是對(duì)全排列算法的非遞歸實(shí)現(xiàn)與遞歸實(shí)現(xiàn)的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05C++ Invalidaterect()函數(shù)作用案例詳解
這篇文章主要介紹了C++ Invalidaterect()函數(shù)作用案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08C語(yǔ)言實(shí)現(xiàn)注冊(cè)登錄系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)注冊(cè)登錄系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12可能是全網(wǎng)最詳細(xì)的Qt連接MySQL數(shù)據(jù)庫(kù)教程
QT眾所周知是一個(gè)開(kāi)源的,以C++為底層的可視化工具庫(kù),下面這篇文章主要給大家介紹了關(guān)于最詳細(xì)的Qt連接MySQL數(shù)據(jù)庫(kù)教程的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04C/C++實(shí)現(xiàn)發(fā)送與接收HTTP/S請(qǐng)求的示例代碼
HTTP(Hypertext Transfer Protocol)是一種用于傳輸超文本的協(xié)議,它是一種無(wú)狀態(tài)的、應(yīng)用層的協(xié)議,用于在計(jì)算機(jī)之間傳輸超文本文檔,通常在 Web 瀏覽器和 Web 服務(wù)器之間進(jìn)行數(shù)據(jù)通信,本文給大家介紹了C/C++發(fā)送與接收HTTP/S請(qǐng)求,需要的朋友可以參考下2023-11-11C++ OpenCV實(shí)現(xiàn)與添加椒鹽噪聲和高斯噪音
圖像噪聲是圖像在獲取或是傳輸過(guò)程中受到隨機(jī)信號(hào)干擾,妨礙人們對(duì)圖像理解及分析處理的信號(hào),本文為大家整理了C++結(jié)合OpenCV為圖像添加椒鹽噪聲和高斯噪音的代碼,需要的可以收藏一下2023-09-09C++使用ImGUI框架開(kāi)發(fā)一個(gè)簡(jiǎn)單程序
ImGui?是一個(gè)用于C++的用戶界面庫(kù),跨平臺(tái)、無(wú)依賴(lài),支持OpenGL、DirectX等多種渲染API,下面就跟隨小編一起學(xué)習(xí)一下如何使用ImGUI框架開(kāi)發(fā)一個(gè)簡(jiǎn)單程序吧2023-08-08C語(yǔ)言中輸入函數(shù)(scanf()、fgets()和gets())的區(qū)別詳解
這篇文章主要給大家介紹了關(guān)于C語(yǔ)言中三種輸入函數(shù)(scanf()、fgets()和gets())區(qū)別的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11