C++的智能指針你真的了解嗎
什么是RAII
RAII(Resource Acquisition Is Initialization)是由C++之父提出的,中文翻譯為資源獲取即初始化,使用局部對(duì)象來管理資源的技術(shù)稱為資源獲取即初始化;這里的資源主要是指操作系統(tǒng)中有限的東西如內(nèi)存(heap)、網(wǎng)絡(luò)套接字、互斥量、文件句柄等等,局部對(duì)象是指存儲(chǔ)在棧的對(duì)象,它的生命周期是由操作系統(tǒng)來管理的,無需人工介入
RAII的原理
資源的使用一般經(jīng)歷三個(gè)步驟:
- 獲取資源(創(chuàng)建對(duì)象)
- 使用資源
- 銷毀資源(析構(gòu)對(duì)象)
但是資源的銷毀往往是程序員經(jīng)常忘記的一個(gè)環(huán)節(jié),所以程序界就想如何在程序中讓資源自動(dòng)銷毀呢?解決問題的方案就是:RAII,它充分的利用了C++語言局部對(duì)象自動(dòng)銷毀的特性來控制資源的生命周期
裸指針存在的問題
1.難以區(qū)分指向的是單個(gè)對(duì)象還是一個(gè)數(shù)組
2.使用完指針之后無法判斷是否應(yīng)該銷毀指針,因?yàn)闊o法判斷指針是否”擁有“指向的對(duì)象
3.在已經(jīng)確定需要銷毀指針的情況下,也無法確定是用delete關(guān)鍵字刪除,還是有其他特殊的銷毀機(jī)制,例如通過將指針傳入某個(gè)特定的銷毀函數(shù)來摧毀指針
4.即使已經(jīng)確定了銷毀指針的方法,由于1的原因,仍然無法確定到底是i用delete(銷毀單個(gè)對(duì)象)還是delete[](銷毀一個(gè)數(shù)組)
5.假設(shè)上述的問題都解決了,也很難保證在代碼的所有路徑中(分支結(jié)構(gòu),異常導(dǎo)致的挑戰(zhàn)),有且僅有一次銷毀指針的操作;任何一條路徑遺漏都可能導(dǎo)致內(nèi)存的泄露,而銷毀多次則會(huì)導(dǎo)致未定義行為
6.理論上沒有方法來分辨一個(gè)指針是否處于懸掛狀態(tài)
auto_ptr
class Object
{
int value;
public:
Object(int x = 0) :value(x)
{
cout << "Create Object:" << this << endl;
}
~Object()
{
cout << "Destory Object:" << this << endl;
}
int& Value()
{
return value;
}
};
template<class _Ty>
class my_auto_ptr
{
private:
bool _Owns;
_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;
}
};
void fun()
{
my_auto_ptr<Object> obj(new Object(10));
}
int main()
{
fun();
}


在這里將Object構(gòu)建完成后,將其指針給到p,當(dāng)函數(shù)結(jié)束去調(diào)動(dòng)智能指針的析構(gòu)函數(shù)去釋放空間
若我們需要在fun()函數(shù)中,去調(diào)用Object類的方法obj->Value();
class Object
{
int value;
public:
Object(int x = 0) :value(x)
{
cout << "Create Object:" << this << endl;
}
~Object()
{
cout << "Destory Object:" << this << endl;
}
int& Value()
{
return value;
}
};
template<class _Ty>
class my_auto_ptr
{
private:
bool _Owns;
_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 ->()const
{
return get();
}
};
void fun()
{
my_auto_ptr<Object> obj(new Object(10));
cout << obj->Value() << endl;
cout << (*obj).Value() << endl;
}
int main()
{
fun();
}

通過運(yùn)算符重載,(*obj) 后將直接指向堆區(qū)(heap)的對(duì)象實(shí)體
若我們通過一個(gè)my_auto_ptr去創(chuàng)建另一個(gè)my_auto_ptr
class Object
{
int value;
public:
Object(int x = 0) :value(x)
{
cout << "Create Object:" << this << endl;
}
~Object()
{
cout << "Destory Object:" << this << endl;
}
int& Value()
{
return value;
}
};
template<class _Ty>
class my_auto_ptr
{
private:
bool _Owns;
_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;
}
my_auto_ptr(const my_auto_ptr& obj):_Owns(obj._Owns),_Ptr(obj._ptr)
{
}
my_auto_ptr& operator=(const my_auto_ptr& _Y)
{
if(this == &_Y) return *this;
if(_Owns)
{
delete _Ptr;
}
_Owns = _Y._Owns;
_Ptr = _Y._Ptr;
return 0;
}
_Ty* get()const
{
return _Ptr;
}
_Ty& operator*()const
{
return *(get());
}
_Ty* operator ->()const
{
return get();
}
void reset(_Ty* p = NULL)
{
if (_Owns)
{
delete _Ptr;
}
_Ptr = p;
}
_Ty* release()const
{
_Ty* tmp = NULL;
if (_Owns)
{
((my_auto_ptr*)this)->_Owns = false; //常性進(jìn)行修改
tmp = _Ptr;
((my_auto_ptr*)this)->_Ptr = NULL;
}
return tmp;
}
};
void fun()
{
my_auto_ptr<Object> pobja(new Object(10));
my_auto_ptr<Object> pobjb(pobja);
}
int main()
{
fun();
}
如果通過淺拷貝,則兩個(gè)指針擁有同一個(gè)資源,在析構(gòu)的過程會(huì)造成資源的重復(fù)釋放導(dǎo)致崩潰
若設(shè)置為將其資源進(jìn)行轉(zhuǎn)移
my_auto_ptr(const my_auto_ptr& obj):_Owns(obj._Owns),_Ptr(release())
{
}
my_auto_ptr& operator=(const my_auto_ptr& _Y)
{
if(this == &_Y) return *this;
if(_Owns)
{
delete _Ptr;
}
_Owns = _Y._Owns;
_Ptr = _Y.release();
return 0;
}
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;
}
那么上面的過程中,資源會(huì)進(jìn)行轉(zhuǎn)移pobja將不再擁有資源,導(dǎo)致pobja失去資源進(jìn)而程序崩潰
這也就是auto_ptr的局限性,也導(dǎo)致該智能指針的幾乎沒有使用
unique_ptr
該智能指針屬于唯一性智能指針,將拷貝構(gòu)造刪除,也就不能將其新建另一個(gè)對(duì)象,同時(shí)也不能作為參數(shù)傳入
class Object
{
int value;
public:
Object(int x = 0) :value(x)
{
cout << "Create Object:" << this << endl;
}
~Object()
{
cout << "Destory Object:" << this << endl;
}
int& Value()
{
return value;
}
};
int main()
{
std::unique_ptr<Object> pobja(new Object(10));
//std::unique_ptr<Object> pobjb(pobja); error
//不允許
std::unique_ptr<Object> pobjb(std::move(pobja));
}
通過移動(dòng)賦值是可以的,通過明確的概念,對(duì)其資源進(jìn)行轉(zhuǎn)移
同時(shí)unique_ptr可以區(qū)分其所指向的是一個(gè)單獨(dú)空間,或者是連續(xù)的空間
struct delete_ar_object
{
void operator()(Object* op)
{
if(op == NULL) return;
delete[] op;
}
}
int main()
{
std::unique_ptr<Object> pobja(new Object(10));
std::unique_ptr<Object,delete_ar_object> pobjb(new Object[10]);
}
在這里如果是連續(xù)空間,會(huì)調(diào)用刪除連續(xù)空間的刪除器;單獨(dú)空間則使用默認(rèn)刪除器
unique_ptr在編寫的時(shí)候,有多個(gè)模板類,分別對(duì)應(yīng)單個(gè)對(duì)象的方案和一組對(duì)象的方案
并且可以通過智能指針指向fopen打開的文件對(duì)象,而文件對(duì)象是同fclose去進(jìn)行關(guān)閉的
struct delete_file
{
void operator()(FILE *fp)
{
if(fp == NULL) return;
fclose(fp);
}
}
std::unique_ptr<FILE,delete_file> pfile(fopen("zyq.txt","w"));
這里只需要將默認(rèn)的刪除器,更改為對(duì)文件對(duì)象的刪除器
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
xxx_cast類型轉(zhuǎn)換的實(shí)現(xiàn)方法
下面小編就為大家?guī)硪黄獂xx_cast類型轉(zhuǎn)換的實(shí)現(xiàn)方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12
C語言練習(xí)題:求1到10的階乘之和簡(jiǎn)單實(shí)例
下面小編就為大家?guī)硪黄狢語言練習(xí)題:求1到10的階乘之和簡(jiǎn)單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-05-05
C++數(shù)據(jù)精度問題(對(duì)浮點(diǎn)數(shù)保存指定位小數(shù))
這篇文章主要介紹了對(duì)浮點(diǎn)數(shù)保存指定位小數(shù)。比如, 1.123456. 要保存1位小數(shù),,調(diào)用方法后, 保存的結(jié)果為: 1.1。 再比如,1.98765, 保存2位小數(shù)的結(jié)果為: 2.00,需要的朋友可以參考下2017-08-08
使用C# 判斷給定大數(shù)是否為質(zhì)數(shù)的詳解
本篇文章是對(duì)使用C#判斷給定大數(shù)是否為質(zhì)數(shù)的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
C++單例模式為何要實(shí)例化一個(gè)對(duì)象不全部使用static
這篇文章主要介紹了C++單例模式為何要實(shí)例化一個(gè)對(duì)象不全部使用static,文基于C++圍繞主題展開詳細(xì)內(nèi)容,需要的小伙伴可以參考一下2022-05-05
C語言之如何用isspace()和ungetc()實(shí)現(xiàn)前導(dǎo)空白字符過濾
這篇文章主要介紹了C語言如何用isspace()和ungetc()實(shí)現(xiàn)前導(dǎo)空白字符過濾問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04

