RAII:資源分配及初始化。但是這個翻譯并沒有顯示出這個慣用法的真正內(nèi)涵。RAII的好處在于它提供了一種資源自動管理的方式,當出現(xiàn)異常,回滾等現(xiàn)象時,RAII可以正確的釋放資源。
合山網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián)公司,合山網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為合山數(shù)千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)網(wǎng)站制作要多少錢,請找那個售后服務(wù)好的合山做網(wǎng)站的公司定做!
內(nèi)存泄漏會導(dǎo)致:
1.內(nèi)存耗盡 2.其他程序可能用不了了 3.程序崩潰
在資源的獲取和釋放之間,我們通常會使用資源,但常常一些不可預(yù)計的異常會在資源使用過程中產(chǎn)生,這就使資源沒有得到正確的釋放。但是我們其實是可以捕捉異常的,但是捕捉異常會使得代碼冗余雜亂,量大而且可讀性比較低。
比如:
void DoSomething() { int *p=new int(1); cout<<"DoSomething()"<<endl; } void Test() { try { DoSomething(); } catch(...) { delete p; throw; } }
這樣短短一個代碼看起來卻很雜亂,而且也不好控制。
所以我們引出了智能指針。智能指針的實現(xiàn)原理就是RAII。
智能指針(smart pointer)是存儲指向動態(tài)分配(堆)對象指針的類,用于生存期控制,能夠確保自動正確的銷毀動態(tài)分配的對象,防止內(nèi)存泄露。它的一種通用實現(xiàn)技術(shù)是使用引用計數(shù)(reference count)。智能指針類將一個計數(shù)器與類指向的對象相關(guān)聯(lián),引用計數(shù)跟蹤該類有多少個對象共享同一指針。每次創(chuàng)建類的新對象時,初始化指針并將引用計數(shù)置為1;當對象作為另一對象的副本而創(chuàng)建時,拷貝構(gòu)造函數(shù)拷貝指針并增加與之相應(yīng)的引用計數(shù);對一個對象進行賦值時,賦值操作符減少左操作數(shù)所指對象的引用計數(shù)(如果引用計數(shù)為減至0,則刪除對象),并增加右操作數(shù)所指對象的引用計數(shù);調(diào)用析構(gòu)函數(shù)時,構(gòu)造函數(shù)減少引用計數(shù)(如果引用計數(shù)減至0,則刪除基礎(chǔ)對象)。
下面介紹幾個Boost庫中的智能指針。
AutoPtr
AutoPtr的實現(xiàn)主要是管理權(quán)轉(zhuǎn)移。它沒有考慮引用計數(shù),當用一個對象構(gòu)造另一個對象時,會轉(zhuǎn)移這種擁有關(guān)系。
template<class T> class AutoPtr { //friend ostream& operator<< <T>(ostream& os, const AutoPtr<T>& ap); public: AutoPtr(T* ptr) :_ptr(ptr) {} ~AutoPtr() { if (_ptr!=NULL) { delete _ptr; } } AutoPtr(AutoPtr<T>& ap) :_ptr(ap._ptr) { ap._ptr = NULL; } public: AutoPtr<T>& operator=(const AutoPtr<T>& ap) { if (this != ap) { delete _ptr; _ptr = ap._ptr; ap._ptr = NULL; return _ptr; } } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } public: T* GetPtr() { return _ptr; } private: T* _ptr; };
舊版的AutoPtr在它的成員函數(shù)中有一個變量owner,在構(gòu)架對象的時候owner為true。當用它去構(gòu)建新的對象時,他自己的owner變?yōu)閒alse,新對象的owner為true。賦值重載的時候也是,新對象的owner是true。這樣在析構(gòu)的時候只要判斷owner的狀態(tài)是否為true,當為true時,將這塊空間delete即可。
template<class T> class AutoPtr { public: AutoPtr(T* ptr) :_ptr(ptr) , owner(true) {} ~AutoPtr() { if (owner) { delete _ptr; } } AutoPtr(AutoPtr<T>& ap) :_ptr(ap._ptr) , owner(true) { ap._ptr = NULL; ap.owner = false; } public: AutoPtr<T>& operator=(const AutoPtr<T>& ap) { if (this != ap) { delete _ptr; _ptr = ap._ptr; ap.owner = false; owner = true; return _ptr; } } }
看起來舊的比新的好。AutoPtr最大的缺點就是只能有一個指針管理一塊空間。那么為什么新的還取代了舊的呢?看下面
AutoPtr<int> ap1(new int(1)); if (1) { AutoPtr<int> ap2(ap1); } *ap1 = 3;
這段代碼是用舊版本實現(xiàn)的智能指針(ap1)指向一個動態(tài)開辟的內(nèi)存,然后在if條件語句中又有一個ap2指向這塊內(nèi)存,我們會知道,根據(jù)舊版的智能指針的實現(xiàn)原理,ap1的_owner為false,ap2的_owner為true。那么除了if條件語句的局部作用域,ap2就自動調(diào)用析構(gòu)函數(shù)釋放內(nèi)存,那么當我們在外面*ap1=3時,訪問到的是一塊已經(jīng)被釋放了的內(nèi)存,那么程序這時就會出現(xiàn)問題。
如果是新版的auto_ptr,它提供了一個公有成員函數(shù)GetPtr(),可以獲取指針_ptr,當發(fā)生這種情況時,它可以先判斷_ptr是否為空,然后才去訪問內(nèi)存。舊版本這樣做是無用的,因為ap1的_ptr并不為空。
2.ScopePtr//守衛(wèi)指針
這個類型的指針簡單來說就是簡單粗暴的防拷貝。
template<class T> class ScopedPtr { public: ScopedPtr(T* ptr) :_ptr(ptr) {} ~ScopedPtr() { if (_ptr != NULL) { delete _ptr; } } protected: ScopedPtr(const ScopedPtr<T>& sp); ScopedPtr<T>& operator=(ScopedPtr<T>& sp); public: T& operator*() { return *_ptr; } T* operator->() { return _ptr; } private: T* _ptr; };
將拷貝構(gòu)造函數(shù)和賦值重載函數(shù)只聲明不實現(xiàn),而且是Protected.就是為了防止在類外實現(xiàn)的情況發(fā)生,簡單粗暴哈哈。
3.SharedPtr
采用引用計數(shù),構(gòu)造一個對象時計數(shù)器為1,用這個對象去拷貝構(gòu)造另一個新的對象時,計數(shù)器增加1.去賦值給另一個對象時,計數(shù)器同樣加1.析構(gòu)時計數(shù)器減1.當計數(shù)器值為1時,便可delete這塊空間。
template<class T> class SharedPtr { public: SharedPtr(T* ptr) :_ptr(ptr) , _pcount(new int(1)) {} ~SharedPtr() { _Release(); } SharedPtr(SharedPtr<T>& sp) { _ptr = sp._ptr; _pcount = sp._pcount; (*_pcount)++; } public: SharedPtr<T>& operator=(const SharedPtr<T>& sp) { if (_ptr != sp._ptr) { _Release(); _ptr = sp._ptr; _pcount = sp._pcount; (*_pcount)++; } return *this; } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } long UseCount() { return *(_pcount); } T* GetPtr() { return _ptr; } private: T* _ptr; int* _pcount; Del _del; void _Release() { if (--(*_pcount) == 0) { _del(_ptr); delete(_pcount); } } };
在這些指針中最常用到的就是SharedPtr了。但是SharedPtr也存在問題。
線程安全(這個問題我這個級別還不能很好地解決哈哈。等著我變成大神,就辦了它。。。)
循環(huán)引用
什么是循環(huán)引用呢??給小伙伴舉個例子來說明一下。
void test2() { SharedPtr<Node> cur(new Node(1)); SharedPtr<Node> next(new Node(2)); cur->_next = next; next->_prev = cur; } struct Node { Node(int d) :_data(d) , _next(NULL) , _prev(NULL) {} int _data; SharedPtr<Node> _next; SharedPtr<Node> _prev; }; int main() { test2(); getchar(); return 0; }
運行這段代碼回發(fā)現(xiàn)并沒有輸出。沒有輸出的原因是 cur,next的引用計數(shù)都是2,當出了test2的作用域時,分別調(diào)用析構(gòu)函數(shù),二者的引用計數(shù)都減為1.但是cur靠next->_prev指著,next靠cur->_next指著,兩個都在等對方先delete自己的引用計數(shù)才能減到0,自己才能delete,這就導(dǎo)致二者都不會delete了。要解決這個問題呢,我們就要引用第四種只能指針了。。那就是WeakPtr了。WeakPtr可以說就是為了SharedPtr準備的。因為WeakPtr的構(gòu)造函數(shù)只接受SharedPtr類型的對象。
struct Node { Node(int d) :_data(d) , _next(NULL) , _prev(NULL) {} int _data; WeakPtr<Node> _next; WeakPtr<Node> _prev; };
WeakPtr不增加引用計數(shù)。這樣next->_prev和cur->_next兩個指針就不會增加引用計數(shù),也就不會出現(xiàn)循環(huán)引用的問題了。
3.定制刪除器
當SharedPtr類型的指針遇到其他有些類型的指針時,它就不行了。。。。哦哦。。為什么說他不行了呢,因為SharedPtr只能解決new 出來的東西。那么對于文件類型的指針呢,malloc出來的東西呢。這就需要我們?yōu)樗麄兞可矶ㄖ平鉀Q方法啦。
在這里,又要講到另外一個知識點了。那就是仿函數(shù)。它就是能把對象當做函數(shù)調(diào)用一樣的使用。
template<class T> struct Less { bool operator()(const T& l,const T& r) { retrun l>r; } } int main() { Less less; cout<<less(1.2)<<endl;//使用對象去調(diào)用類里面的函數(shù) }
接下來就是定制刪除器
template<class T ,class Del = Delete<T>> class SharedPtr { public: SharedPtr(T* ptr) :_ptr(ptr) , _pcount(new int(1)) {} SharedPtr(T* ptr,Del del) :_ptr(ptr) , _pcount(new int(1)) , _del(del) {} ~SharedPtr() { _Release(); } SharedPtr(SharedPtr<T,Del>& sp) { _ptr = sp._ptr; _pcount = sp._pcount; (*_pcount)++; } public: SharedPtr<T, Del>& operator=(const SharedPtr<T, Del>& sp) { if (_ptr != sp._ptr) { _Release(); _ptr = sp._ptr; _pcount = sp._pcount; (*_pcount)++; } return *this; } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } long UseCount() { return *(_pcount); } T* GetPtr() { return _ptr; } private: T* _ptr; int* _pcount; Del _del; void _Release() { if (--(*_pcount) == 0) { _del(_ptr); delete(_pcount); } } }; template<class T> struct Free //free { void operator()(void* ptr) { cout << "free" << endl; free(ptr); ptr = NULL; } }; template<class T> struct Delete //delete { void operator()(T* ptr) { cout << "Del" << endl; delete(ptr); ptr = NULL; } }; template<class T> struct Fclose //fclose { void operator()(void* ptr) { cout << "Fclose" << endl; fclose((FILE*)ptr); } };
最后我們來總結(jié)一下:
AutoPtr 管理權(quán)轉(zhuǎn)移-》不要使用
ScopePtr 防拷貝-》簡單粗暴
SharedPtr 引用計數(shù)-》增減引用計數(shù),最有一個對象釋放
xxxArray 管理對象數(shù)組-》operator[]
WearPtr 弱指針,輔助SharedPtr.解決循環(huán)引用
說了這么多,比較啰嗦哈
網(wǎng)頁題目:RAII&智能指針
標題鏈接:http://muchs.cn/article36/jpjcsg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗、定制網(wǎng)站、面包屑導(dǎo)航、自適應(yīng)網(wǎng)站、關(guān)鍵詞優(yōu)化、網(wǎng)頁設(shè)計公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)