shared_ptr的出現:
使用裸指針對堆對象(動態內存)進行管理是極其容易出現問題的。例如:忘記釋放內存造成的內存泄漏、尚有指針引用內存的情況下釋放了該內存等等的問題。
為此能夠更加智能地保留或者釋放堆(動態)對象,標准庫以及boost庫提供了智能指針。智能指針負責自動釋放所指向的對象。智能指針的使用和普通指針類似,解引用一個智能指針返回它指的對象。
shared_ptr:允許多個指針指向同一個對象。
shared_ptr用法:
#include <iostream> #include <memory> using namespace std; class Test { public: Test(string s):_str(s) { cout << "Test create" << endl; } ~Test() { cout << "Test delete" << endl; } string &getStr() { return _str; } void setStr(string s) { _str = s; } void print() { cout << _str << endl; } private: string _str; };
int main() { shared_ptr<Test> p1 = make_shared<Test>("pTest1"); shared_ptr<Test> p2 = make_shared<Test>("pTest2"); shared_ptr<Test> p3 = make_shared<Test>("pTest3"); p3 = p1; p2 = p1; cout << p3.use_count() << endl; cout << p1.use_count() << endl; return 0; }
將p1智能指針賦值給了p3背后發生的事情:p3所指向的對象引用計數-1后為0,釋放p3所指向的對象。p1的引用計數+1。
問題:通過p3可以拿到所指向的引用計數值為3,通過p1也能拿到所指向的引用計數為3。是否p1智能指針對象和p3智能指針對象都保存了一份引用計數值呢?
參考《c++ primer第五版》P402頁所給出的解釋:
到底是用一個計數器還是其他數據結構來記錄有多少指針共享對象, 完全由標准庫的具體實現來決定。關鍵是智能指針類能記錄有多少個shared_ptr指向相同的對象,並能在恰當的時候自動釋放對象。
雖然智能指針能自動釋放內存,但是使用不當同樣會導致內存泄漏。
shared_ptr的循環引用問題:
循環引用問題模型:
問題描述:
如果有一個類A和類B,其數據成員是一個shared_ptr指向彼此。那么此時類A和類B的引用計數ref為1。如果此時又有兩個智能指針分別指向A和B,那么此時類A和類B的引用計數為2,當這兩個智能指針離開其作用域的時候ref減為1,但並不會釋放智能指針所指向的對象。會造成內存泄漏。
#include <iostream> #include <memory> using namespace std; class B; class A { public: ~A() { cout << "A delete" << endl; } shared_ptr<B> ptr; }; class B { public: ~B() { cout << "B delete" << endl; } shared_ptr<A> ptr; }; int main() { while(1) { shared_ptr<A> pa(new A()); //A對象的引用計數ref=1 shared_ptr<B> pb(new B()); //B對象的引用計數ref=1 pa->ptr = pb; //B對象的引用計數ref=2 pb->ptr = pa; //A對象的引用計數ref=2 } //離開作用域后,雖然pa和pb智能指針對象釋放了,但由於其所指對象的引用計數為1而未被釋放,故造成內存泄漏。 }
weak_ptr的出現:
為了解決循環引用的問題,出現了弱引用的weak_ptr。weak_ptr指向對象並不會對引用計數+1。weak_ptr不對其所指的對象進行內存資源的管理。解決循環引用的方法就是將shared_ptr的數據成員改為weak_ptr。
weak_ptr的用法:
當創建一個weak_ptr時,要用一個shared_ptr來初始化它:
shared_ptr<int> p = make_shared<int>(111); weak_ptr wp(p);
因為是弱引用,創建wp不會改變p的引用計數。有可能weak_ptr所指向的對象不存在了,因此無法直接通過weak_ptr指針訪問其所指向的對象,應該通過調用lock()方法將weak_ptr提升為一個shared_ptr,再訪問其所指向的對象。如果提升失敗那么指向的對象已被釋放。
if (shared_ptr<int> p = wp.lock()) { //..... }