std::shared_ptr使用引用計數,每一個shared_ptr的拷貝都指向相同的內存,在最後一個shared_ptr析構的時候,內存纔會釋放。ios
shared_ptr能夠經過make_shared來初始化,也能夠經過shared_ptr<T>輔助函數和reset方法來初始化。智能指針的用法和普通指針的用法相似,不過不須要本身管理分配的內存,對於沒有初始化的指針,只能經過reset來初始化,當智能指針有值,reset會使計數器減1。智能指針能夠經過重載的bool來判斷是否爲空。數組
#include <iostream> #include <memory> using namespace std; int main() { //智能指針初始化 shared_ptr<int> p = make_shared<int>(20); shared_ptr<int> p(new int(1)); shared_ptr<int> p1 = p; shared_ptr<int> ptr; //所指的對象會被重置,不帶參數則是銷燬 ptr.reset(new int(5)); if(ptr) { cout << "ptr is not null" << endl; } return 0; }
智能指針不能經過原始指針來初始化:安全
shared_ptr<int> p = new int(1); //編譯報錯,不能直接賦值
當須要獲取原始指針的時候,能夠經過get來返回原始指針。不能釋放,若是釋放會出錯。函數
shared_ptr<int> ptr(new int(1)); int* p = ptr.get(); delete p; //error
智能指針支持指定刪除器,在指針引用爲0的時候自動調用。支持普通函數和lambda表達式。this
//普通函數 void DeleteIntPtr(int *p) {delete p;} shared_ptr<int> p(new int(10), DeleteIntPtr);
//lambda表達式 shared_ptr<int> p(new int(10), [](int *p) {delete p;});
當智能指針管理動態數組的時候,默認的刪除器不支持數組對象。須要指定刪除器,自定義刪除器或者使用改善的默認修改器均可以。spa
shared_ptr<int> p(new int[10], [](int *p) {delete[] p;}); //lambda shared_ptr<int> p1(new int[10], default_delete<int []>); //指定delete []
a.避免一個原始指針初始化多個shared_ptr。指針
int* p = new int; shared_ptr<int> p1(p); shared_ptr<int> p2(p);
b.不要在參數實參中建立shared_ptr。code
func(shared_ptr<int>(new int), g());
不一樣的編譯器可能有不一樣的調用約定,若是先new int,而後調用g(),在g()過程當中發生異常,可是shared_ptr沒有建立,那麼int的內存就會泄漏,正確的寫法應該是先建立智能指針。對象
shared_ptr<int> p(new int); f(p, g());
c.避免循環使用,循環使用可能致使內存泄漏blog
#include <iostream> #include <memory> using namespace std; struct A; struct B; struct A { shared_ptr<B> bptr; ~A() { cout << "A is deleted." << endl; } }; struct B { shared_ptr<A> aptr; ~B() { cout << "B is deleted." << endl; } }; int main() { shared_ptr<A> ap(new A); shared_ptr<B> bp(new B); ap->bptr = bp; bp->aptr = ap; return 0; }
這個最經典的循環引用的場景,結果是兩個指針A和B都不會刪除,存在內存泄漏。循環引用致使ap和bp的引用計數爲2,離開做用域以後,ap和bp的引用計數爲1,並不會減0,致使兩個指針都不會析構而產生內存泄漏。
d.經過shared_from_this()返回this指針。不要將this指針做爲shared_ptr返回出來,由於this指針本質是一個裸指針,這樣可能致使重複析構。
#include <iostream> #include <memory> using namespace std; struct A { shared_ptr<A> GetSelf() { return shared_ptr<A>(this); } ~A() { cout << "A is deleted." << endl; } }; int main() { shared_ptr<A> ap(new A); shared_ptr<A> ap2 = ap->GetSelf(); return 0; } //執行結果 A is deleted. A is deleted.
這個例子中,因爲同一指針(this)構造了兩個只能指針ap和ap2,而他們之間是沒有任何關係的,在離開做用域以後this將會被構造的兩個智能指針各自析構,致使重複析構的錯誤。固然,也有解決辦法,解決辦法在以後的weak_ptr介紹。
unique_ptr是一個獨佔型智能指針,它不容許其餘的智能指針共享其內部的指針,不容許經過賦值將一個unique_ptr賦值給另外一個unique_ptr。只能經過函數來返回給其它的unique_ptr,好比move函數,可是轉移以後,再也不對以前的指針具備全部權。
unique_ptr<int> uptr(new int(10)); unique_ptr<int> uptr2 = uptr; //error unique_ptr<int> uptr3 = move(uptr); //uptr將變爲null
unique_ptr和shared_ptr相比除了獨佔以外,unique_ptr還能夠指向一個數組。
unique_ptr<int []> ptr(new int[10]); //ok ptrp[1] = 10; shared_ptr<int []> ptr2(new int[10]); //error
unique_ptr必須指定刪除器類型,不像shared_ptr那樣直接指定刪除器。
shared_ptr<int> ptr(new int(1), [](int *p){delete p;}); //ok unique_ptr<int> ptr2(new int(1), [](int *p){delete p;}); //error unique_ptr<int, void(*)(int *)> ptr2(new int(1), [](int *p){delete p;}); //ok
經過指定函數類型,而後經過lambda表達式實現是能夠,可是若是捕獲了變量將會編譯報錯,由於lambda表達式在沒有捕獲變量的狀況下能夠直接轉換爲函數指針,可是捕獲了變量就沒法轉換。若是要支持,能夠經過std::function來解決。
unique_ptr<int, void(*)(int *)> ptr2(new int(1), [&](int *p){delete p;}); //error unique_ptr<int, std::function<void(int*)>> ptr2(new int(1), [&](int *p){delete p;}); //ok
unique_ptr支持自定義刪除器。
#include <iostream> #include <memory> #include <functional> using namespace std; struct DeleteUPtr { void operator()(int* p) { cout << "delete" << endl; delete p; } }; int main() { unique_ptr<int, DeleteUPtr> p(new int(1)); return 0; }
弱引用智能指針weak_ptr用來監視shared_ptr,不會使引用技術加1,也無論理shared_ptr內部的指針,主要是監視shared_ptr的生命週期。weak_ptr不共享指針,不能操做資源,它的構造和析構都不會改變引用計數。
經過use_count()方法來得到當前資源的引用計數。
shared_ptr<int> sp(new int(10)); weak_ptr<int> wp(sp); cout << wp.use_count() << endl; //輸出1
shared_ptr<int> sp(new int(10)); weak_ptr<int> wp(sp); if(wp.expired()) { cout << "sp 已經釋放,無效" << endl; } else { cout << "sp 有效" << endl; }
能夠經過lock方法來獲取所監視的shared_ptr。
#include <iostream> #include <memory> using namespace std; weak_ptr<int> gw; void f() { //監聽是否釋放 if(gw.expired()) { cout << "gw is expired." << endl; } else { auto spt = gw.lock(); cout << *spt << endl; } } int main() { { auto p = make_shared<int>(20); gw = p; f(); } f(); return 0; } //執行結果 20 gw is expired.
sharerd_ptr不能直接返回this指針,須要經過派生std::enable_shared_from_this類,並經過其方法shared_from_this來返回智能指針,由於std::enable_shared_from_this類中有一個weak_ptr,這個weak_ptr用來觀測this指針,調用shared_from_this方法時,調用了內部的weak_ptr的lock()方法,將所觀測的sharerd_ptr返回。
#include <iostream> #include <memory> using namespace std; struct A:public enable_shared_from_this<A> { shared_ptr<A> GetSelf() { return shared_from_this(); } ~A() { cout << "A is deleted." << endl; } }; int main() { shared_ptr<A> spy(new A); shared_ptr<A> p = spy->GetSelf(); //ok return 0; } //執行結果 A is deleted.
在外面建立A對象的智能指針經過該對象返回this的智能指針是安全的,由於shared_from_this()是內部weak_ptr調用lock()方法以後返回的智能指針,在離開做用域以後,spy的引用計數爲0,A對象會被析構,不會出現A對象被析構兩次的問題。
須要注意的是,獲取自身智能指針的函數僅在share_ptr<T>的構造函數調用以後才能使用,由於enable_shared_from_this內部的weak_ptr只有經過shared_ptr才能構造。
shared_ptr的循環引用可能致使內存泄漏,以前的例子再也不贅述,經過weak_ptr能夠解決這個問題,怎麼解決呢?答案是,將A或者B任意一個成員變量改成weak_ptr便可。
#include <iostream> #include <memory> using namespace std; struct A; struct B; struct A { shared_ptr<B> bptr; ~A() { cout << "A is deleted." << endl; } }; struct B { weak_ptr<A> aptr; ~B() { cout << "B is deleted." << endl; } }; int main() { shared_ptr<A> ap(new A); shared_ptr<B> bp(new B); ap->bptr = bp; bp->aptr = ap; return 0; } //執行結果 A is deleted. B is deleted.
這樣在對B成員賦值時,即bp->aptr = ap,因爲aptr是weak_ptr,並不會增長引用計數,因此ap的計數仍然是1,在離開做用域以後,ap的引用計數會減爲0,A指針會被析構,析構以後,其內部的bptr引用計數會減1,而後離開做用域以後,bp引用計數從1減爲0,B對象也被析構,因此不會發生內存泄漏。