柚子快報(bào)激活碼778899分享:C++RAII內(nèi)存管理技術(shù)
柚子快報(bào)激活碼778899分享:C++RAII內(nèi)存管理技術(shù)
文章目錄
一.什么是RAII內(nèi)存管理技術(shù)?二.智能指針unique_ptrshared_ptr循環(huán)引用問(wèn)題weak_ptr
一.什么是RAII內(nèi)存管理技術(shù)?
C++在引入異常機(jī)制后,代碼執(zhí)行流的跳轉(zhuǎn)變得難以預(yù)料,如果使用普通的指針進(jìn)行內(nèi)存管理,很難避免內(nèi)存泄漏的問(wèn)題(執(zhí)行流跳轉(zhuǎn)導(dǎo)致堆區(qū)資源無(wú)法被釋放)RAII技術(shù)指的是利用對(duì)象的生命周期來(lái)管理內(nèi)存資源,就堆區(qū)內(nèi)存資源的管理而言,指的就是:將指針?lè)庋b在類中,在類對(duì)象構(gòu)造時(shí)獲取堆區(qū)資源,當(dāng)類對(duì)象生命周期結(jié)束時(shí),通過(guò)類對(duì)象的析構(gòu)函數(shù)自動(dòng)完成堆區(qū)資源的釋放,這樣的類對(duì)象就是智能指針
智能指針可以有效地避免開(kāi)發(fā)中出現(xiàn)內(nèi)存泄漏的問(wèn)題,同時(shí)為開(kāi)發(fā)者省去了很多時(shí)間和精力
二.智能指針
C++11標(biāo)準(zhǔn)庫(kù)中智能指針主要分為三大類:
unique_ptr
unique_ptr對(duì)象之間不允許進(jìn)行拷貝,即一個(gè)unique_ptr對(duì)象管理一塊堆區(qū)內(nèi)存資源,是一一對(duì)應(yīng)的關(guān)系unique_ptr的簡(jiǎn)單實(shí)現(xiàn)原理:
//不允許拷貝的智能指針
//delfunc是堆區(qū)資源釋放函數(shù),用于調(diào)用delete或者delete[]
template
class unique_ptr
{
public:
unique_ptr(T * ptr = nullptr)
:_ptr(ptr)
{}
~unique_ptr()
{
if (_ptr)
{
delfunc del;
del(_ptr);
std::cout << "delete" << std::endl;
}
}
//讓智能指針可以像指針一樣被使用
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
//禁止unique_ptr對(duì)象間的拷貝
unique_ptr(const unique_ptr
unique_ptr
private:
T* _ptr;
};
使用指針管理堆區(qū)資源時(shí),我們會(huì)讓其指向單個(gè)對(duì)象或者對(duì)象數(shù)組,單個(gè)對(duì)象用delete完成資源釋放,而對(duì)象數(shù)組要采用delete[]完成資源釋放.unique_ptr的類模板參數(shù)delfunc的設(shè)計(jì)目的就是為了區(qū)分上述兩種情況,讓使用者通過(guò)設(shè)計(jì)仿函數(shù)的方式在delete和delete[]之間做選擇
shared_ptr
shared_ptr是允許進(jìn)行拷貝的智能指針,然而shared_ptr對(duì)象之間發(fā)生拷貝時(shí),就會(huì)出現(xiàn)多個(gè)智能指針對(duì)象同時(shí)管理同一塊堆區(qū)內(nèi)存的情況,shared_ptr通過(guò)引用計(jì)數(shù)的技術(shù)避免了同一塊堆區(qū)資源被釋放多次的情況出現(xiàn):引用計(jì)數(shù)的實(shí)現(xiàn)思路:
在shared_ptr內(nèi)部封裝一個(gè)指針int * recount用來(lái)維護(hù)引用計(jì)數(shù)變量每當(dāng)一個(gè)shared_ptr對(duì)象進(jìn)行構(gòu)造并申請(qǐng)堆區(qū)資源時(shí),同時(shí)在堆區(qū)申請(qǐng)一個(gè)int變量作為該shared_ptr對(duì)象所指向的堆區(qū)資源的引用計(jì)數(shù)變量每當(dāng)shared_ptr被拷貝時(shí),連同int * recount一起拷貝,然后讓引用計(jì)數(shù)變量加一即可(賦值重載的實(shí)現(xiàn)還要考慮引用計(jì)數(shù)變量減一和堆區(qū)資源釋放的問(wèn)題)每當(dāng)shared_ptr析構(gòu)時(shí),引用計(jì)數(shù)變量減一,若引用計(jì)數(shù)變量減為0,則釋放堆區(qū)資源 shared_ptr的簡(jiǎn)單實(shí)現(xiàn)原理:
//允許拷貝的智能指針,利用引用計(jì)數(shù)解決內(nèi)存管理沖突
//delfunc是堆區(qū)資源釋放函數(shù),用于調(diào)用delete或者delete[]
template
class shared_ptr
{
public:
//構(gòu)造智能指針(同時(shí)創(chuàng)建引用計(jì)數(shù)變量)
shared_ptr(T* ptr = nullptr)
:_ptr(ptr),
_recount(new int(1))
{}
shared_ptr(const shared_ptr
: _ptr(shptr._ptr),
_recount(shptr._recount)
{
//智能指針拷貝增加引用計(jì)數(shù)
(*_recount)++;
}
shared_ptr
{
if (_ptr != shptr._ptr)
{
//智能指針指向改變,其先前指向的堆區(qū)內(nèi)存和引用計(jì)數(shù)變量需要處理
Release();
//完成智能指針拷貝并增加引用計(jì)數(shù)
_ptr = shptr._ptr;
_recount = shptr._recount;
++(*_recount);
}
return (*this);
}
~shared_ptr()
{
Release();
}
//讓智能指針可以像指針一樣被使用
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* getptr() const
{
return _ptr;
}
private:
//將資源釋放函數(shù)進(jìn)行封裝,方便復(fù)用
//引用計(jì)數(shù)減一,若引用計(jì)數(shù)減為0則釋放資源
void Release()
{
//引用計(jì)數(shù)減一
--(*_recount);
//引用計(jì)數(shù)為0則釋放資源
if (*_recount == 0)
{
if (_ptr)
{
delfunc del;
del(_ptr);
std::cout << "delete" << std::endl;
}
//同時(shí)注意釋放引用計(jì)數(shù)變量
delete _recount;
}
}
private:
T* _ptr;
int* _recount;
};
shared_ptr類內(nèi)部同時(shí)還要解決引用計(jì)數(shù)變量帶來(lái)的線程安全的問(wèn)題,這里暫不討論
循環(huán)引用問(wèn)題
shared_ptr用在自引用結(jié)構(gòu)體中會(huì)出現(xiàn)對(duì)象無(wú)法析構(gòu)的問(wèn)題:
template
class deletefunc
{
public:
void operator()(T* ptr)
{
delete ptr;
}
};
// 循環(huán)引用
struct ListNode
{
int _val;
shared_ptr
shared_ptr
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
// 循環(huán)引用
void test_shared_cycle()
{
share_ptr
share_ptr
n1->_next = n2;
n2->_prev = n1;
}
當(dāng)test_shared_cycle()函數(shù)執(zhí)行完后,兩個(gè)鏈表節(jié)點(diǎn)都無(wú)法正常析構(gòu),shared_ptr引發(fā)了循環(huán)引用的問(wèn)題:n1節(jié)點(diǎn)的析構(gòu)依賴于n2節(jié)點(diǎn)中_prev指針的析構(gòu),n2節(jié)點(diǎn)中_prev指針的析構(gòu)依賴于n2節(jié)點(diǎn)的析構(gòu),n2節(jié)點(diǎn)的析構(gòu)又依賴于n1節(jié)點(diǎn)中_next指針的析構(gòu), n1節(jié)點(diǎn)中_next指針的析構(gòu)又依賴于n1節(jié)點(diǎn)的析構(gòu).如此往復(fù)構(gòu)成了循環(huán)的依賴關(guān)系導(dǎo)致兩個(gè)節(jié)點(diǎn)都無(wú)法被釋放.weak_ptr就是為了解決循環(huán)引用問(wèn)題而設(shè)計(jì)的
weak_ptr
weak_ptr智能指針不參與堆區(qū)內(nèi)存的申請(qǐng)和釋放,也不會(huì)增加特定內(nèi)存塊的引用計(jì)數(shù)(其功能和普通指針差不多),weak_ptr支持以shared_ptr為引用形參的拷貝構(gòu)造和賦值weak_ptr簡(jiǎn)單實(shí)現(xiàn)原理:
//用于配合share_ptr的使用用于循環(huán)引用的場(chǎng)景
//delfunc是堆區(qū)資源釋放函數(shù),用于調(diào)用delete或者delete[]
//weak_ptr不參與資源的申請(qǐng)和釋放
template
class weak_ptr
{
public:
weak_ptr()
:_ptr(nullptr)
{}
~weak_ptr()
{}
weak_ptr(const weak_ptr
:_ptr(wptr._ptr)
{}
weak_ptr(const share_ptr
:_ptr(shptr.getptr())
{}
weak_ptr
{
_ptr = wptr._ptr;
return (*this);
}
weak_ptr
{
_ptr = shptr.getptr();
return (*this);
}
//讓智能指針可以像指針一樣被使用
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
在自引用結(jié)構(gòu)體中采用weak_ptr就可以避免出現(xiàn)循環(huán)引用的問(wèn)題
柚子快報(bào)激活碼778899分享:C++RAII內(nèi)存管理技術(shù)
好文推薦
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。