C++智能指針的使用
智能指針的使用場(chǎng)景
1. 下面的程序中,new了以后,我們也delete了,但是因?yàn)閽伄惓?dǎo)致后面的delete沒(méi)有得到執(zhí)行,所以就內(nèi)存泄漏了,所以我們需要new以后捕獲異常,捕獲到異常后delete內(nèi)存,再把異常拋出。
2.但是因?yàn)閚ew本身也可能拋異常,連續(xù)的兩個(gè)new和下面的Divide都可能會(huì)拋異常,讓我們處理起來(lái)很麻煩。智能指針?lè)诺竭@樣的場(chǎng)景里面就讓問(wèn)題簡(jiǎn)單多了。
double Divide(int a, int b)
{
// 當(dāng)b == 0時(shí)拋出異常
if (b == 0)
{
throw "Divide by zero condition!";
}
else
{
return (double)a / (double)b;
}
}
void Func()
{
// 這?可以看到如果發(fā)?除0錯(cuò)誤拋出異常,另外下?的array和array2沒(méi)有得到釋放。
// 所以這?捕獲異常后并不處理異常,異常還是交給外?處理,這?捕獲了再重新拋出去。
// 但是如果array2new的時(shí)候拋異常呢,就還需要套?層捕獲釋放邏輯,這?更好解決?案
// 是智能指針,否則代碼太戳了
int* array1 = new int[10];
int* array2 = new int[10];// 拋異常呢
// 連續(xù)的兩個(gè)new
// 如果第一個(gè)new拋異??梢圆东@
// 如果第二個(gè)new拋異常,可以解決delete第二個(gè)new
// 但是第一個(gè)new沒(méi)有被delete
// 這樣就需要再第二個(gè)new的地方捕獲異常,delete第一個(gè)new
// 如果有很多個(gè)new呢,就太麻煩了,這里就需要智能指針了
try
{
int len, time;
cin >> len >> time;
cout << Divide(len, time) << endl;
}
catch (...)
{
cout << "delete []" << array1 << endl;
cout << "delete []" << array2 << endl;
delete[] array1;
delete[] array2;
throw; // 異常重新拋出,捕獲到什么拋出什么
}
// ...
cout << "delete []" << array1 << endl;
delete[] array1;
cout << "delete []" << array2 << endl;
delete[] array2;
}
int main()
{
try
{
Func();
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
catch (const exception& e)
{
cout << e.what() << endl;
}
catch (...)
{
cout << "未知異常" << endl;
}
return 0;
}
RAII和智能指針
1. RAII是獲取到資源立即初始化,它是一種管理資源的類的設(shè)計(jì)思想,本質(zhì)是一種利用對(duì)象生命周期來(lái)管理獲取到的動(dòng)態(tài)資源,避免資源泄漏,這里的資源可以是內(nèi)存、文件指針、網(wǎng)絡(luò)連接、互斥鎖等等。RAII在獲取資源時(shí)把資源委托給一個(gè)對(duì)象,接著控制對(duì)資源的訪問(wèn),資源在對(duì)象的生命周期內(nèi)始終保持有效,最后在對(duì)象析構(gòu)的時(shí)候釋放資源,這樣保障了資源的正常釋放,避免資源泄漏問(wèn)題。
2. RAII像指針一樣
在對(duì)象銷毀后會(huì)正常的釋放資源
template<class T>
class SmartPtr
{
public:
// RAII
SmartPtr(T* ptr)
:_ptr(ptr)
{}
~SmartPtr()
{
cout << "delete[] " << _ptr << endl;
delete[] _ptr;
}
private:
T* _ptr;
};
double Divide(int a, int b)
{
// 當(dāng)b == 0時(shí)拋出異常
if (b == 0)
{
throw "Divide by zero condition!";
}
else
{
return (double)a / (double)b;
}
}
void Func()
{
// 這?可以看到如果發(fā)?除0錯(cuò)誤拋出異常,另外下?的array和array2沒(méi)有得到釋放。
// 所以這?捕獲異常后并不處理異常,異常還是交給外?處理,這?捕獲了再重新拋出去。
// 但是如果array2new的時(shí)候拋異常呢,就還需要套?層捕獲釋放邏輯,這?更好解決?案
// 是智能指針,否則代碼太戳了
SmartPtr<int> p1 = new int[10];
SmartPtr<int> p2 = new int[10];// 拋異常呢
SmartPtr<int> p3 = new int[10];
int len, time;
cin >> len >> time;
cout << Divide(len, time) << endl;
}
int main()
{
try
{
Func();
}
catch (const char* errmsg)
{
cout << errmsg << endl;
}
catch (const exception& e)
{
cout << e.what() << endl;
}
catch (...)
{
cout << "未知異常" << endl;
}
return 0;
}
智能指針類除了滿足RAII的設(shè)計(jì)思路,還要方便資源的訪問(wèn),所以智能指針類還像迭代器類一樣重載 operator*/operator->/operator[] 等運(yùn)算符,方便訪問(wèn)資源。
template<class T>
class SmartPtr
{
public:
// RAII
SmartPtr(T* ptr)
:_ptr(ptr)
{}
~SmartPtr()
{
cout << "delete[] " << _ptr << endl;
delete[] _ptr;
}
// 重載運(yùn)算符,模擬指針的?為,?便訪問(wèn)資源
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T& operator[](size_t i)
{
return _ptr[i];
}
private:
T* _ptr;
};
void Func()
{
p1[1] = 50;
p4->first = 1;
p4->second = 2;
cout << p1[1] << endl;
}
C++標(biāo)準(zhǔn)庫(kù)智能指針的使用
淺拷貝的問(wèn)題,共同管理同一塊資源,而深拷貝是兩個(gè)指針管理不同的資源了,現(xiàn)在要求兩個(gè)指針管理同一塊資源?
int main()
{
// 需要p1和p2同時(shí)管理同一塊資源,淺拷貝
// 析構(gòu)多次的問(wèn)題如何解決?
SmartPtr<int> p1 = new int[10];
SmartPtr<int> p2(p1);
return 0;
}
1.C++標(biāo)準(zhǔn)庫(kù)中的智能指針都在< memory >這個(gè)頭文件下,智能指針有好幾種,除了weak_ptr他們都符合RAII和像指針一樣訪問(wèn)的行為,原理上而言主要是解決智能指針拷貝時(shí)的思路不同。
2. C++98的智能指針
auto_ptr是C++98時(shí)設(shè)計(jì)出來(lái)的智能指針,它的特點(diǎn)是拷貝時(shí)把被拷貝對(duì)象的資源的管理權(quán)轉(zhuǎn)移給拷貝對(duì)象,這是?個(gè)非常糟糕的設(shè)計(jì),因?yàn)樗麜?huì)把被拷貝對(duì)象懸空(相當(dāng)于被拷貝對(duì)象是空指針了),訪問(wèn)報(bào)錯(cuò)的問(wèn)題,C++11設(shè)計(jì)出新的智能指針后,強(qiáng)烈建議不要使用auto_ptr
struct Date
{
int _year;
int _month;
int _day;
Date(int year = 1, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
// 本身不需要析構(gòu),為了驗(yàn)證auto_ptr是否可以解決析構(gòu)兩次的問(wèn)題
~Date()
{
cout << "~Date()" << endl;
}
};
int main()
{
// 拷?時(shí),管理權(quán)限轉(zhuǎn)移,被拷?對(duì)象ap1懸空
auto_ptr<Date> ap1(new Date);
auto_ptr<Date> ap2(ap1);
// 空指針訪問(wèn),ap1對(duì)象已經(jīng)懸空
// ap1->_year++;
return 0;
}
C++11的智能指針
2、unique_ptr是唯一指針,他的特點(diǎn)是不支持拷貝,只支持移動(dòng)。如果不需要拷貝的場(chǎng)景就非常建議使用它。把拷貝構(gòu)造和拷貝賦值給(封)delete了
int main()
{
unique_ptr<Date> up1(new Date);
不支持拷貝
// unique_ptr<Date> up2(up1);
支持移動(dòng),移動(dòng)后up1也懸空了
unique_ptr<Date> up3(move(up1));
return 0;
}
3、shared_ptr共享指針,他的特點(diǎn)是支持拷貝,也支持移動(dòng)。如果需要拷貝的場(chǎng)景就需要使用他了。底層是用引用計(jì)數(shù)的方式實(shí)現(xiàn)的。
int main()
{
shared_ptr<Date> p1(new Date);
shared_ptr<Date> p2(p1);
shared_ptr<Date> p3(p1);
cout << p1.use_count() << endl;// 3
p1->_day++;
cout << p1->_day << endl; // 2
cout << p2->_day << endl; // 2
cout << p3->_day << endl; // 2
return 0;
}
4、weak_ptr弱(輔助解決shared_ptr的一個(gè)問(wèn)題)指針,它不同于上面的指針,它不支持RAII,也就意味著不能用它直接管理資源,weak_ptr的產(chǎn)生本質(zhì)是要解決shared_ptr的一個(gè)循環(huán)引用導(dǎo)致內(nèi)存泄漏的問(wèn)題。
到此這篇關(guān)于C++智能指針的使用的文章就介紹到這了,更多相關(guān)C++智能指針內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++算術(shù)運(yùn)算符與類型轉(zhuǎn)換
這篇文章主要介紹了C++算術(shù)運(yùn)算符與類型轉(zhuǎn)換,C++當(dāng)中提供5種基礎(chǔ)的算術(shù)運(yùn)算符,分別是加法、減法、乘法、除法和取模。下main我們就一起來(lái)看看下面文章得具體舉例與說(shuō)明,需要的朋友可以參考一下,希望對(duì)你有所幫助2021-11-11
C語(yǔ)言實(shí)現(xiàn)掃雷小游戲(擴(kuò)展版可選擇游戲難度)
游戲目標(biāo)是找出所有沒(méi)有地雷的方格,完成游戲;要是按了有地雷的方格,游戲失??;玩家可標(biāo)記雷的位置,游戲以完成時(shí)間來(lái)評(píng)高低,并且用戶可以選擇游戲難度2019-10-10
C語(yǔ)言實(shí)現(xiàn)套接字操作的示例代碼
在C語(yǔ)言中,套接字(Socket)是一種進(jìn)行網(wǎng)絡(luò)通信的接口,這篇文章就來(lái)和大家介紹一下C語(yǔ)言中是如何進(jìn)行套接字操作的,需要的可以參考一下2023-06-06
C語(yǔ)言編程中生成隨機(jī)數(shù)的入門(mén)教程
這篇文章主要介紹了C語(yǔ)言編程中生成隨機(jī)數(shù)的入門(mén)教程,包括利用rand()函數(shù)來(lái)編寫(xiě)隨機(jī)數(shù)生成器的示例,要的朋友可以參考下2015-12-12
C++?Qt開(kāi)發(fā)之使用QUdpSocket實(shí)現(xiàn)組播通信
Qt?是一個(gè)跨平臺(tái)C++圖形界面開(kāi)發(fā)庫(kù),利用Qt可以快速開(kāi)發(fā)跨平臺(tái)窗體應(yīng)用程序,本文將重點(diǎn)介紹如何運(yùn)用QUdpSocket組件實(shí)現(xiàn)基于UDP的組播通信,感興趣的可以了解下2024-03-03
C語(yǔ)言中改變目錄的相關(guān)操作函數(shù)詳解
這篇文章主要介紹了C語(yǔ)言中改變目錄的相關(guān)操作函數(shù)詳解,分別是fchdir()函數(shù)和rewinddir()函數(shù)的使用方法,需要的朋友可以參考下2015-09-09

