RAII資源分配即初始化,定義一個類來封裝資源的分配和釋放,在構(gòu)造 函數(shù)完成資源的分配和初始化,在析構(gòu)函數(shù)完成資源的清理,可以保證資源的正確初始化和釋放。
成都創(chuàng)新互聯(lián)公司專注于企業(yè)營銷型網(wǎng)站建設(shè)、網(wǎng)站重做改版、渠縣網(wǎng)站定制設(shè)計、自適應(yīng)品牌網(wǎng)站建設(shè)、H5網(wǎng)站設(shè)計、商城開發(fā)、集團公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計等建站業(yè)務(wù),價格優(yōu)惠性價比高,為渠縣等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
智能指針的引入:
由于return ,throw等關(guān)鍵字的存在,導(dǎo)致順序執(zhí)行流的錯亂,不斷的進行跳轉(zhuǎn),使開辟的空間
看似被釋放,而實際上導(dǎo)致內(nèi)存的泄露。
例如以下兩個例子:
void Test1()
{
int *p1 = new int(1);
if (1)
{
return;
}
delete p1;
}
void DoSomeThing()
{ //...
throw 2 ;
//...
}
void Test2 ()
{
int* p1 = new int(2);
//...
try
{
DoSomeThing();
}
catch(...)
{
//delete p1 ;
throw;
}
//...
delete p1 ;
}以上兩個例子,看似new與delete結(jié)合使用,但是實際上已經(jīng)導(dǎo)致內(nèi)存的泄露,為了解決以上問題,就需要在寫此類代碼時需要多加小心,但是對于大量的代碼開發(fā),想要注意就很難了。
于是乎就引入了智能指針:
所謂智能指針就是智能/自動化的管理指針?biāo)赶虻膭討B(tài)資源的釋放。
c++庫函數(shù)中的智能指針為(auto_ptr,unique_ptr,shared_ptr);
Boost庫函數(shù)中的智能指針為(auto_ptr,scoped_ptr,shared_ptr)。
接下來就一起來了解一下智能指針的發(fā)展歷史。
1、在vc6.0之前的版本中auto_ptr的模擬實現(xiàn)方式
template<class T>
class OldAutoPtr
{
public:
OldAutoPtr(T *ptr)
:_ptr(ptr)
, _owner(true)
{}
//拷貝構(gòu)造
OldAutoPtr(OldAutoPtr& ap)
:_ptr(ap._ptr)
,_owner(ap._owner)
{
ap._owner = false;//權(quán)限的轉(zhuǎn)讓
}
//運算符重載
OldAutoPtr& operator=(OldAutoPtr& ap)
{
if (ap._ptr != _ptr)//自賦值和指向同一份空間
{
if (_ptr)
{
delete _ptr;
}
_ptr = ap._ptr;
_owner = ap._owner;//權(quán)限的轉(zhuǎn)讓
ap._owner = false;
}
return *this;
}
//析構(gòu)函數(shù)
~OldAutoPtr()
{
if (_owner)//只刪除擁有權(quán)限的指針
{
delete _ptr;
}
}
public:
T& operator *()
{
return *_ptr;
}
T* operator ->()
{
return _ptr;
}
private:
T* _ptr;
bool _owner;//權(quán)限擁有者
};評價:
看似最初版本的auto_ptr已經(jīng)很接近了原聲指針的使用,多個指針都可以指向一份空間,而且釋放的同時不會導(dǎo)致同一份空間的多次釋放。但是在下面的情境中卻發(fā)生了致命的危害:
void TestOldAutoPtr()
{
OldAutoPtr<int> ap1(new int(1));
if (true)
{
OldAutoPtr<int> ap2(ap1);
//OldAutoPtr<int> ap3(new int(2));
//ap3 = ap2;
}
//ap1將析構(gòu)的權(quán)限給予了ap2,ap1,ap2指向同一份空間,ap2在出了if作用域之后ap2對象釋放,進而導(dǎo)致ap1也被釋放。
//但是在if作用域之外,又對ap1(ap2)指向的空間進行簡引用,導(dǎo)致程序崩潰,如果不使用的話則會造成指針的懸掛(野指針)。
*ap1 = 10;
}針對以上的狀況,在之后的版本中對auto_ptr進行了完全的權(quán)限轉(zhuǎn)移,轉(zhuǎn)移之后該指針不能使用。
2、現(xiàn)在的版本auto_ptr的實現(xiàn)
template<typename T>
class AutoPtr
{
public:
AutoPtr(T* ptr)
:_ptr(ptr)
{}
AutoPtr(AutoPtr<T>& ap)
:_ptr(ap._ptr)
{
ap._ptr = NULL;//權(quán)限轉(zhuǎn)移
}
AutoPtr<T>& operator=(AutoPtr<T> & ap)
{
if (this != &ap)//自賦值
{
if (_ptr)
{
delete _ptr;
}
_ptr = ap._ptr;
ap._ptr = NULL;//權(quán)限轉(zhuǎn)移
}
return *this;
}
~AutoPtr()
{
if (_ptr != NULL)
{
delete _ptr;
_ptr = NULL;
}
}
public:
T& operator * ()//沒有參數(shù)
{
return *_ptr;
}
T* operator->()//沒有參數(shù)
{
return _ptr;
}
private:
T* _ptr;
};評價:
這種實現(xiàn)方式很好的解決了old的版本特殊場景的野指針問題,但是它與原生指針的差別更大,由于實現(xiàn)了完全的權(quán)限轉(zhuǎn)移,所以導(dǎo)致在拷貝構(gòu)造和賦值之后只有一個指針可以使用,而其他指針都置為NULL,使用很不方便,而且還很容易導(dǎo)致對于NULL指針的截引用,導(dǎo)致程序崩潰,其危害也是比較大的。
3、針對以上的問題,在Boost庫中引入了簡單粗暴的解決方法scoped_ptr,直接不允許其拷貝構(gòu)造和賦值(ps:新的C++11標(biāo)準(zhǔn)中叫做unique_ptr)
解決辦法:將賦值和拷貝構(gòu)造函數(shù)只聲明,不實現(xiàn),切記,將其聲明為protected或者private以免在類的外部對其進行破環(huán)性處理,同時也是提高了代碼的安全性。
template<typename T>
class ScopedPtr
{
public:
ScopedPtr(T* ptr)
:_ptr(ptr)
{}
~ScopedPtr()
{
if (_ptr != NULL)
{
delete _ptr;
}
}
protected://將其聲明為protected或者private不能聲明為public
ScopedPtr(ScopedPtr<T>& sp);
ScopedPtr<T>operator=(ScopedPtr<T>&sp);
public:
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T *_ptr;
};評價:
在一般的情況下,如果不需要對于指針的內(nèi)容進行拷貝,賦值操作,而只是為了防止內(nèi)存泄漏的發(fā)生,該只能指針完全可以滿足需求。
4、允許拷貝構(gòu)造和賦值的shared_ptr模擬實現(xiàn)
解決辦法:
通過為每個空間多開辟4個字節(jié)做為引用計數(shù)器,在拷貝構(gòu)造、賦值、析構(gòu)時用計數(shù)器來解決。
template<typename T>
class SharedPtr
{
public:
SharedPtr(T* ptr)
:_ptr(ptr)
, _pCount(new int(1))
{}
SharedPtr(const SharedPtr<T>& sp)
{
_ptr = sp._ptr;
_pCount = sp._pCount;
(*_pCount)++;
}
SharedPtr<T>operator=(SharedPtr<T> sp)
{
swap(_ptr, sp._ptr);
swap(_pCount, sp._pCount);
return *this;
}
~SharedPtr()
{
_Realse();
}
public:
void _Realse()
{
if (--(*_pCount) == 0)
{
delete _ptr;
delete _pCount;
}
}
public:
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
int* _pCount;
};評價:
簡化版智能指針SharedPtr看起來不錯,但是實際上存在以下問題:
1)循環(huán)引用
2)定置刪除器
循環(huán)引用

template<class T>
struct Node
{
public:
~Node()
{
cout << "delete:" << this << endl;
}
public:
shared_ptr<Node> _prev;
shared_ptr<Node> _next;
/*weak_ptr<Node> _prev;
weak_ptr<Node> _next;*/
};
void test_round()//循環(huán)引用
{
shared_ptr<Node> cur(new Node());
shared_ptr<Node> next(new Node());
cout << "連接前:" << endl;
cout << "cur:" << cur.use_count() << endl;
cout << "next:" << next.use_count() << endl;
cur->_next = next;
next->_prev = cur;
cout << "連接后:" << endl;
cout << "cur:" << cur.use_count() << endl;
cout << "next:" << next.use_count() << endl;
/*shared_ptr<Node> cur(new Node());
weak_ptr<Node> wp1(cur);*/
}
解決辦法:
使用一個弱引用智能指針(weak_ptr)來打破循環(huán)引用(weak_ptr不增加引用計數(shù))
template<class T>
struct Node
{
public:
~Node()
{
cout << "delete:" << this << endl;
}
public:
//shared_ptr<Node> _prev;
//shared_ptr<Node> _next;
weak_ptr<Node> _prev;
weak_ptr<Node> _next;
};
void test_round()//循環(huán)引用
{
shared_ptr<Node> cur(new Node());
shared_ptr<Node> next(new Node());
cout << "連接前:" << endl;
cout << "cur:" << cur.use_count() << endl;
cout << "next:" << next.use_count() << endl;
cur->_next = next;
next->_prev = cur;
cout << "連接后:" << endl;
cout << "cur:" << cur.use_count() << endl;
cout << "next:" << next.use_count() << endl;
/*shared_ptr<Node> cur(new Node());
weak_ptr<Node> wp1(cur);*/
}
定置刪除器
在shared_ptr中只能處理釋放new開辟的空間,而對于malloc,以及fopen打開的文件指針不能處理,為了能夠全面的處理各種各樣的指針,所以提出了定制刪除器,而其實現(xiàn)則是通過仿函數(shù)(通過對()運算符的重載)來實現(xiàn)。
1)模擬實現(xiàn)的解決
template<typename T,typename D>
class SharedPtr
{
public:
SharedPtr(T* ptr)
:_ptr(ptr)
, _pCount(new int(1))
{}
SharedPtr(T* ptr, D del)
:_ptr(ptr)
, _pCount(new int(1))
, _del(del)
{}
SharedPtr(const SharedPtr<T, D>& sp)
{
_ptr = sp._ptr;
_pCount = sp._pCount;
(*_pCount)++;
}
SharedPtr<T, D>operator=(SharedPtr<T, D> sp)
{
swap(_ptr, sp._ptr);
swap(_pCount, sp._pCount);
return *this;
}
~SharedPtr()
{
_Realse();
}
public:
void _Realse()
{
if (--(*_pCount) == 0)
{
_del(_ptr);
delete _pCount;
}
}
public:
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
int* _pCount;
D _del;
};
template<typename T>
struct DeafaultDel
{
void operator()(T* ptr)
{
cout << "DeafaultDel:" << ptr << endl;
}
};
template<typename T>
struct Free
{
void operator()(T*ptr)
{
cout << "Free:" << ptr << endl;
}
};
template<typename T>
struct Fclose
{
void operator()(T* ptr)
{
cout << "Fclose:" << ptr << endl;
fclose(ptr);
}
};
//測試代碼
void TestDelete()
{
int *p1 = (int*)malloc(sizeof(int));
SharedPtr<int,Free<int>> sp1(p1);
FILE* p2 = fopen("test.txt", "r");
SharedPtr<FILE, Fclose<FILE>> sp2(p2);
}測試結(jié)果

2)系統(tǒng)shared_ptr的解決
struct Free
{
void operator()(void * ptr)
{
cout << "Free:" << ptr << endl;
free(ptr);
}
};
struct Fclose
{
void operator ()(FILE* ptr)
{
cout << "Fclose" << ptr << endl;
fclose(ptr);
}
};
//測試代碼
void test_shared_ptr_delete()
{
int *p1 = (int*)malloc(sizeof(int));
shared_ptr<int> sp1(p1,Free());
FILE* p2 = fopen("test.txt", "r");
shared_ptr<FILE> sp2(p2,Fclose());//崩潰
}測試結(jié)果

總結(jié):
1、如果需要使用智能指針的話,scoped_ptr完全可以勝任。在非常特殊的情況下,例如對STL容器對象,應(yīng)該只使用shared_ptr,任何情況下都不要使用auto_ptr.
2、使用時盡量局部化,因為其是通過調(diào)用其析構(gòu)函數(shù)來實現(xiàn)資源的回收。
網(wǎng)站標(biāo)題:c++智能指針的不斷演化
網(wǎng)站URL:http://chinadenli.net/article8/gsgeop.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化、面包屑導(dǎo)航、品牌網(wǎng)站建設(shè)、虛擬主機、電子商務(wù)、做網(wǎng)站
聲明:本網(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)