C++智能指针详解【C++智能指针】
- 自動(dòng)內(nèi)存管理
- 智能指針
- 什么是 RAII
- 原理
- 智能指針的模板(template)實(shí)現(xiàn)
- auto_ptr
- auto_ptr 使用
- 重載函數(shù) operator-> / *語法格式
- 自實(shí)現(xiàn) auto_ptr
- auto_ptr被廢棄的原因
- unique_ptr
- unique_ptr使用
- 常用接口
- shared_ptr
- 原理
- 常用接口
- 移動(dòng)和傳遞引用
- 小結(jié)
- unique_ptr可用于托管堆內(nèi)存數(shù)組。(官方僅支持unique_ptr)
- shared_ptr不可用于托管堆內(nèi)存數(shù)組。(官方不支持)
自動(dòng)內(nèi)存管理
智能指針
一套成熟的類庫,通常都會(huì)引用內(nèi)存管理機(jī)制。
例如:Cocos,Qt等
C++在語言層面也提供了內(nèi)存管理機(jī)制,即智能指針 auto_ptr。
雖然 auto_ptr 己經(jīng) deserted 了(引自 Google C++ 編程規(guī)范),但是它的后繼者,諸如 share_ptr, weak_ptr 靈感均取自于auto_ptr。
什么是 RAII
RAII(Resource Acquisition Is Initialization),也稱為"資源獲取即初始化",是 C++語言的一種管理資源、避免泄漏的慣用法。
RAII是一種利用對象生命周期來控制程序資源的簡單技術(shù)。
程序資源包括:內(nèi)存、文件句柄、網(wǎng)絡(luò)連接、互斥量等等。
C++中的構(gòu)造器和析構(gòu)器就是根據(jù)RAII思想設(shè)計(jì)。
C++標(biāo)準(zhǔn)保證任何情況下,已構(gòu)造的對象最終會(huì)銷毀,即它的析構(gòu)函數(shù)最終會(huì)被調(diào)用。
有構(gòu)造就一定有析構(gòu)。
RAII一般做法:
簡單的說,RAII 的做法是使一個(gè)對象,在其構(gòu)造時(shí)獲取資源,在對象生命期控制對資源的訪問使之始終保持有效,最后在對象生命期結(jié)束時(shí)析構(gòu)進(jìn)行資源釋放。
注意理解:在類對象包裝其他對象資源時(shí),通過NEW獲取其他對象資源,通過構(gòu)造進(jìn)行其他對象資源初始化,通過析構(gòu)在其他對象消失時(shí)進(jìn)行資源釋放。
其實(shí)也就是把管理一份資源的責(zé)任托管給了一個(gè)對象。
好處:
①:不需要顯示地釋放資源。
②:對象所需的資源在生命周期內(nèi)始終保持有效。
所托管的資源,隨對象的創(chuàng)建而獲取,隨對象的消失而消失。
也就是所謂RAII思想:資源獲取即初始化。
原理
代理了被托管的對象指針,管理對象的生命周期,即實(shí)現(xiàn)自動(dòng)釋放。
其行為類似于所托管的對象指針,原因是重載了operator->和operator*。
智能指針的模板(template)實(shí)現(xiàn)
template <class T> class SmartPtr { public:explicit SmartPtr(T* pointee) : pointee_(pointee){}~SmartPtr(){delete pointee_;}T& operator*() const{//...return *pointee_;}T* operator->() const{//...return pointee_;}private:T* pointee_;//... };auto_ptr
auto_ptr 使用
代碼演示:
#include <iostream> #include <memory>class A { public:A(){std::cout << "A()" << std::endl; //構(gòu)造器}~A(){std::cout << "~A()" << std::endl; //析構(gòu)器}void func(){std::cout << "hahaha" << std::endl; //類內(nèi)成員函數(shù)}};void foo() {A* p = new A; //new 資源獲得:會(huì)直接調(diào)用構(gòu)造器進(jìn)行初始化。delete p; //delete 資源釋放:需要手動(dòng)釋放。//一旦申請資源過多,業(yè)務(wù)邏輯代碼比較復(fù)雜的時(shí)候,在釋放的時(shí)候就可能會(huì)漏掉。std::auto_ptr<A> pp(new A);//資源獲取即初始化。// //上面//pp(new A):括號內(nèi)部就是資源獲取,并且會(huì)直接調(diào)用pp對象的構(gòu)造器進(jìn)行初始化。//也就是new進(jìn)行資源獲取后就直接進(jìn)行初始化。 理解:資源獲取即初始化。//按照使用指針的方式使用pp->func();(*pp).func();//重點(diǎn):離開作用域,對象生命周期結(jié)束,會(huì)自動(dòng)釋放對象資源。 }int main() {foo();return 0; }運(yùn)行結(jié)果:
我們可以看到上面使用智能指針,new申請空間之后自動(dòng)調(diào)用構(gòu)造器初始化并且在對象結(jié)束后自動(dòng)調(diào)用析構(gòu)器釋放申請的內(nèi)存空間。也就是自動(dòng)調(diào)用構(gòu)造器和析構(gòu)器。
重載函數(shù) operator-> / *語法格式
類名& operator * ()
類名 * operator->()
自實(shí)現(xiàn) auto_ptr
代碼演示:
#include <iostream> #include <memory>class A { public:A(){std::cout << "A()" << std::endl; //構(gòu)造器初始化資源}~A(){std::cout << "~A()" << std::endl; //析構(gòu)銷毀資源}void func(){std::cout << "hahaha" << std::endl; //類內(nèi)成員函數(shù)}};class myauto_ptr //管理資源A的智能指針 { public:myauto_ptr(A* ptr) //構(gòu)造器初始化資源{_ptr = ptr;}~myauto_ptr() //析構(gòu)銷毀資源{delete _ptr;}A* operator->() //->運(yùn)算符重載{return _ptr;}A & operator*() //*運(yùn)算符重載{return *_ptr;}private:A* _ptr; };void foo() {myauto_ptr sp(new A);sp->func();(*sp).func(); }int main() {foo();return 0; }運(yùn)行結(jié)果:
上面實(shí)現(xiàn)了:
類對象包裝其他對象資源時(shí),通過NEW獲取其他對象資源,通過構(gòu)造進(jìn)行其他對象資源初始化,通過析構(gòu)在其他對象消失時(shí)進(jìn)行資源釋放。
類比上面代碼中: myauto_ptr 類對象包裝 A 類對象資源時(shí),myauto_ptr 類對象通過NEW獲取A 類對象資源,通過myauto_ptr 類對象構(gòu)造進(jìn)行 A 類對象資源初始化,通過myauto_ptr 類對象析構(gòu)在 A 類對象資源消失時(shí)進(jìn)行資源釋放。
. 運(yùn)算符不可重載。
通過重載運(yùn)算符 * 和 -> ,就可以將對象像指針一樣使用。
auto_ptr被廢棄的原因
防止兩個(gè) auto_ptr 對象擁有同一個(gè)對象(一塊內(nèi)存)。
例如:ap 與 ap2 都覺得指針 p 是歸它管。在析構(gòu)時(shí)都試圖刪除 p ,兩次刪除同一個(gè)對象的行為在C++標(biāo)準(zhǔn)中沒有定義。
代碼演示:
#include <iostream> #include <memory>int main() {int* p = new int(10);{std::auto_ptr<int> ap(p); //使用ap管理pstd::cout << *ap << std::endl;}std::auto_ptr<int> ap2(p); //使用ap2管理pstd::cout << *ap2 << std::endl;return 0; }運(yùn)行結(jié)果:
編譯器報(bào)錯(cuò)。
作參數(shù)傳遞的時(shí),亦是會(huì)出現(xiàn)同樣的情況,兩個(gè)指針,對同一段資源產(chǎn)生了引用行為。
基本類型代碼演示:
運(yùn)行結(jié)果:
自定義類類型代碼演示:
運(yùn)行結(jié)果:
析構(gòu)發(fā)生在func(apc);運(yùn)行結(jié)果之后。
apc進(jìn)入func();就是函數(shù)內(nèi)的臨時(shí)變量,在函數(shù)運(yùn)行結(jié)束之后,對于apc進(jìn)行析構(gòu)。
main函數(shù)運(yùn)行結(jié)束時(shí),ap再次析構(gòu)編譯器就會(huì)報(bào)錯(cuò)。
不允許兩個(gè)auto_ptr指針指向相同的對象。
unique_ptr
uniqu_ptr 的使用方法基本上等同于auto_ptr, 不同之處就在于實(shí)現(xiàn)了資源的唯一。既不可以拷貝也不可以賦值,正如其名字一樣。
unique_ptr使用
代碼演示:
#include <iostream> #include <memory> using namespace std;class Copy { public:Copy() :_i(){cout << "Copy()" << endl;}Copy(int i) :_i(new int(i)){cout << "Copy(int i)" << endl;}Copy(const Copy& another):_i(new int(*another._i)){cout << " Copy(const Copy & another)" << endl;}~Copy() {cout << "~Copy()" << endl;}int* _i; };void func(unique_ptr<Copy> upc) {}int main(int argc, char* argv[]) {unique_ptr<Copy> upc(new Copy(10));cout << *upc->_i << endl;unique_ptr<Copy> upc2(upc); //編譯不過func(upc); //編譯不過return 0; }編譯器報(bào)錯(cuò):
常用接口
生命周期隨構(gòu)造者,reset 自動(dòng)析構(gòu)再重新構(gòu)造,get 判斷是否有效、支持放在容器內(nèi);真正意義智能指針。
不論是臨時(shí)變量指針、類成員指針變量…90%的指針都應(yīng)該用這個(gè)。
代碼演示:
運(yùn)行結(jié)果:
shared_ptr
原理
unique_ptr 解決了auto_ptr 中引用同一個(gè)對象的問題,方式就是不可引用同一個(gè)對象。
shared_ptr 解決了auto_ptr 中引用同一個(gè)對象共同擁有一個(gè)資源,但不會(huì)重析構(gòu)的問題。
shared_ptr 原理是在內(nèi)部保持一個(gè)引用計(jì)數(shù),并且僅當(dāng)引用計(jì)數(shù)為0才能被刪除。
不可以用數(shù)組。
常用接口
可以有多個(gè)持有者的共享指針,即所謂引用計(jì)數(shù)型指針,直到最后一個(gè)持有者delete釋放時(shí),其指向的資源才會(huì)真正被釋放。
典型應(yīng)用案例:
如對同一個(gè)全局無鎖隊(duì)列對象由shared_ptr 封裝,多線程的多個(gè)持有者均持有對其的引用。直到全部線程都釋放掉對其的引用時(shí),該無鎖隊(duì)列對象才會(huì)被最終銷毀。
shared_ptr 適合用于管理“全局動(dòng)態(tài)資源”。
代碼演示:
#include <iostream> #include <memory> using namespace std;int main() {shared_ptr<int> sp1 (new int(666));cout << *sp1 << endl;cout << "cout:" << sp1.use_count() << endl;//1{shared_ptr<int> sp2(sp1);cout << *sp2 << endl;cout << "cout:" << sp1.use_count() << endl;//2cout << "cout:" << sp2.use_count() << endl;//2}cout << "cout:" << sp1.use_count() << endl;//1shared_ptr<int> sp3(sp1);cout << *sp3 << endl;cout << "cout:" << sp3.use_count() << endl;//2return 0; }運(yùn)行結(jié)果:
代碼演示:
#include <iostream> #include <memory> using namespace std;class Copy { public:Copy() :_i(){cout << "Copy()" << this << endl;}Copy(int i) :_i(new int(i)){cout << "Copy(int i)" << this << endl;}Copy(const Copy & another) :_i(new int(*another._i)){cout << " Copy(const Copy & another)" << this << endl;}~Copy() {cout << "~Copy()" << this << endl;}void dis(){cout << *_i << endl;}int* _i; };void func(shared_ptr<Copy> upc) {cout << "func cout:" << upc.use_count() << endl;//3upc.reset();cout << "func cout:" << upc.use_count() << endl;//0 }int main(int argc, char* argv[]) {shared_ptr<Copy> sp1 (new Copy);cout << "cout:" << sp1.use_count() << endl;//1sp1.reset();//釋放資源//sp1.reset();//釋放資源 引用計(jì)數(shù)為0后,多次reset引用計(jì)數(shù)仍然為0。//sp1.reset();//釋放資源 引用計(jì)數(shù)為0后,多次reset引用計(jì)數(shù)仍然為0。cout << "cout:" << sp1.use_count() << endl;//0cout << "++++++++++" << endl;shared_ptr<Copy> sp2(new Copy);shared_ptr<Copy> sp3(sp2);shared_ptr<Copy> sp4(sp3);//cout << "cout:" << sp2.use_count() << endl;//3//sp2.reset(); sp2.reset(); sp2.reset(); //sp2釋放三次//cout << "cout:" << sp3.use_count() << endl;//2//重要:reset操作只管理自己身上的引用。cout << "cout:" << sp2.use_count() << endl;//3sp2.reset(); sp3.reset(); sp4.reset(); //資源的引用釋放三次cout << "cout:" << sp2.use_count() << endl;//0cout << "----------" << endl;shared_ptr<Copy> sp5(new Copy);cout << "cout:" << sp5.use_count() << endl;//1{shared_ptr<Copy> sp6(sp5);cout << "cout:" << sp5.use_count() << endl;//2cout << "cout:" << sp6.use_count() << endl;//2cout << "==========" << endl;}cout << "cout:" << sp5.use_count() << endl;//1cout << "**********" << endl;shared_ptr<Copy> sp7(sp5);cout << "cout:" << sp5.use_count() << endl;//2cout << "cout:" << sp7.use_count() << endl;//2func(sp7);cout << "cout:" << sp7.use_count() << endl;//2cout << "//" << endl;if (sp7.unique())cout << "s7 unique\n" << endl;elsecout << "s7 not unique\n" << endl;sp7.reset();cout << "cout:" << sp7.use_count() << endl;//0cout << "cout:" << sp5.use_count() << endl;//1if (sp5.unique())cout << "sp5 unique\n" << endl;elsecout << "sp5 not unique\n" << endl;return 0; }運(yùn)行結(jié)果:
移動(dòng)和傳遞引用
代碼演示:
#include <iostream> #include <memory> using namespace std;class Copy { public:Copy() :_i(){cout << "Copy()" << this << endl;}Copy(int i) :_i(new int(i)){cout << "Copy(int i)" << this << endl;}Copy(const Copy & another) :_i(new int(*another._i)){cout << " Copy(const Copy & another)" << this << endl;}~Copy() {cout << "~Copy()" << this << endl;}void dis(){cout << *_i << endl;}int* _i; };void func(shared_ptr<Copy> & sp) {cout << "func.use_count:" << sp.use_count() << endl;//1 }int main(int argc, char* argv[]) {//moveshared_ptr<Copy> sp2(new Copy);shared_ptr<Copy> sp3(sp2);shared_ptr<Copy> sp4(sp3);cout << "sp2.use_count:" << sp2.use_count() << endl;//3shared_ptr<Copy> spm = std::move(sp2); cout << "spm.use_count:" << spm.use_count() << endl;//3cout << "sp3.use_count:" << sp3.use_count() << endl;//3cout << "sp4.use_count:" << sp4.use_count() << endl;//3//移動(dòng)不會(huì)導(dǎo)致引用計(jì)數(shù)增加。shared_ptr<Copy> sp5(new Copy);cout << "sp5.use_count:" << sp5.use_count() << endl;//1func(sp5);cout << "sp5.use_count:" << sp5.use_count() << endl;//1//傳遞shared_ptr的引用不會(huì)使引用計(jì)數(shù)+1return 0; }運(yùn)行結(jié)果:
小結(jié)
unique_ptr可用于托管堆內(nèi)存數(shù)組。(官方僅支持unique_ptr)
代碼演示:
#include <iostream> #include <memory>using namespace std;class A { public:A(){cout << "A()" << endl;}~A(){cout << "~A()" << endl;}void dis(){cout << "A::void dis()" << endl;} };int main() {unique_ptr<int[]> up(new int[5]{ 1,2,3,4 });for (int i = 0; i < 5; i++)cout << up[i] << endl;unique_ptr<A[]> up2(new A[5]);for (int i = 0; i < 5; i++)up2[i].dis();return 0; }運(yùn)行結(jié)果:
shared_ptr不可用于托管堆內(nèi)存數(shù)組。(官方不支持)
總結(jié)
以上是生活随笔為你收集整理的C++智能指针详解【C++智能指针】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: xml格式解析
- 下一篇: s3c2440移植MQTT