前言:html
近來,學習STL,忽然發現有智能指針,作了一週的學習(工做以外的時間),斷斷續續的學習,特此作下記錄。ios
誕生的緣由:c++
爲了防止內存泄露,和二次釋放的問題。無非就是嫌棄本身管理內存太費勁,能夠寫個更簡單管理堆內存的類。編程
利用C++的特性:安全
類結束會調用析構函數,無非就是棧空間出棧,同時釋放掉動態建立的空間。網絡
智能指針的做用:多線程
將指針封裝成類,利用了一種叫RAII(資源獲取即初始化的技術,聽着有點高大上),重載操做符(->和*),行爲表現的像指針併發
C++ 11 中的智能指針:函數
包含在頭文件<memory>中,分別有三個智能指針,分別爲shared_ptr,unique_ptr,weak_ptr。性能
1)原理:shared_ptr的多個對象指向同一個指針(大可能是new出來的空間指針),該指針使用引用計數,每使用一次,內部計數器加1,每析構一次,內部的引用計數器減1,減爲0的時候,自動刪除指向的堆內存。
2)實現:就是一個模板類,沒事的時候強烈建議看下里面的具體實現,挺有意思的。
3)不要用一個原始指針初始化多個shared_ptr,不然會形成二次釋放同一內存
4)注意避免循環使用,會在下面舉例,並講解。
2.unique_ptr
「惟一」擁有所指對象,同一時刻只能有一個unique_ptr指向給定對象(禁止使用拷貝語義,只能用移動語義將其移動)。對比原始指針,也是利用了RAII的特性。用戶能夠定義delete操做。
#include <iostream> #include <memory> using namespace std; int main() { unique_ptr<int> uptr(new int(10)); //初始化 unique_ptr<int> uptr1 = move(uptr); //轉移全部權 uptr2.release(); //釋放全部權 return 0; }
3.weak_ptr,配合shared_ptr而引入的的智能指針,是弱引用,相對於shared_ptr強引用來講的。看似就像一個觀察者,觀測資源的使用狀況。weak_ptr能夠從一個shared_ptr獲取另外一個weak_ptr來構造,獲取資源的觀察權 。 並不會引發計數加的狀況,成員有use_count(),查看資源的引用計數,expired(),判斷是否指向的資源被釋放, 當返回爲true的時候,這個資源的引用計數爲0,至關於被釋放,反之就沒有被釋放掉。lock(),返回當前分享指 針,計數器並加1.
#include <iostream> #include <memory> using namespace std; int main() { shared_ptr<int> s_ptr = make_shared<int>(10); cout<<s_ptr.use_count() <<endl; weak_ptr<int> wp(s_ptr); cout<<wp.use_count() <<endl; if(!wp.expired()) { shared_ptr s_ptr2 = wp.lock(); //引用計數器加1 *s_ptr = 100; cout<<wp.use_count()<<endl; } } 運行結果: 1 1 2
應用場景及其問題:
1.循環引用,場景:考慮一個簡單的場景--家長和孩子,一個父母有一個孩子,一個孩子有一雙父母。
使用原始指針的實現:
#include <iostream> using namespace std; class Child; class Parent; class Parent { private: Child* myChild; public: void setChild(Child* ch) { this->myChild = ch; } void doSomething() { if(this->myChild) { cout<<"Child alive"<<endl; } } ~Parent() { cout<<"delete myChild"<<endl; delete myChild; } }; class Child { private: Parent* myParent; public: void setParent(Parent* p) { this->myParent = p; } void doSomething() { if(this->myParent) { cout<<"myParent alive"<<endl; } } ~Child() { cout<<"delete myParent"<<endl; delete myParent; } }; int main() { Parent* p = new Parent; Child* c = new Child; p->setChild(c); c->setParent(p); delete c; return 0; }
如何使用智能指針解決該問題呢:引入智能指針,兩個類只要保證一個類是shared_ptr(強引用)一個是weak_ptr(弱引用)
#include <memory> #include <iostream>
class Child; class Parent; class Parent{ private: std::weak_ptr<Child> ChildPtr; public: void setChild(std::shared_ptr<Child> child) { this->ChildPtr = child; } void doSomething() { } ~Parent() {} }; class Child { private: std::shared_ptr<Parent> ParentPtr; public: void setParent(std::shared_ptr<Parent> parent) { this->ParentPtr = parent; } void doSomething() {} ~Child() {} }; int main() { std::weak_ptr<Parent> wpp; std::weak_ptr<Child> wpc; { std::shared_ptr<Parent> p(new Parent); std::shared_ptr<Child> c(new Child); p->setChild(c); c->setParent(p); wpp = p; wpc = c; std::cout<<p.use_count() <<std::endl; std::cout<<c.use_count() <<std::endl; } std::cout <<wpp.use_count() << std::endl; std::cout << wpc.use_count() << std::endl; return 0; }
運行結果:
2
1
0
0
注意若是使用g++編譯,請添加參數-std=c++11(弱引用是C++11引入)
2..返回shared_ptr自己,並不引發計數器加+1
#include<iostream> #include<memory> using namespace std; class Test: public enable_shared_from_this<Test> { public: Test(){} ~Test() {cout <<"~Test()"<<endl;} shared_ptr<Test> sget() { return shared_from_this(); } }; int main() { weak_ptr<Test> wp; { shared_ptr<Test> sp(new Test); wp = sp; cout<<"sp is "<<sp.use_count()<<endl; sp->sget(); cout<<"sp is "<<sp.use_count()<<endl; } cout<<"wp is"<<wp.use_count()<<endl; return 0; return 0; } 運行結果: sp is 1 sp is 1 ~Test() wp is 0
3.註冊銷燬函數
#include<iostream> #include<memory> using namespace std; struct MyStruct { int *p; MyStruct():p(new int(10)){} }; int main() { MyStruct st; { shared_ptr<MyStruct> sp(&st, [](MyStruct *ptr){ delete(ptr->p); ptr->p = nullptr; cout<<"destructed"<<endl; }); } if(st.p != nullptr) cout<<"no destroyed"<<endl; else cout<<"be destroyed"<<endl; return 0; } 運行結果: destructed be destroyed
4.線程安全討論
官方文檔:1)同一個shared_ptr對象能夠被多線程同時讀取
2) 不一樣的shared_ptr對象能夠被多線程同時修改(提起12分注意力,這裏有坑)
3)任何其餘併發訪問的結果都是無定義的(什麼軟,這個目前沒法理解)
對1)來講,都能理解,讀必定是安全,當在2)狀況下,因爲內部shared_ptr有兩個成員,一個計數,一個指向
實際內存的指針,具體內部實現也沒有上鎖,同時操做兩個數據成員,讀寫操做沒法作到原子化, 在多線程編程 中,在多個線程同時訪問同一個shared_ptr的時 候,請加mutex保護。
下面來詳細的分析下爲何:
1)首先看下shared_ptr內存結構,加入該指針指向一個Foo的類
2)考慮一個簡單的場景,有三個shared_ptr<Foo> 對象 x, g,n;
shared_ptr<Foo> g(new Foo); //線程之間共享的shared_ptr
shared_ptr<Foo> x; //線程A的局部變量
shared_ptr<Foo> n(new Foo);//線程B的局部變量
開始:還挺整齊的,符合咱們的預想
線程A執行x = g;即(read g),如下圖示:可是還沒來的急將引用計數+1,切換到線程B
同時線程B執行g=n;(即寫g),以下圖
這個時候就已經將Foo1申請的動態內存歸還給操做系統了,出現空懸指針,以下圖:
最後將回到線程A,以下圖:
如今這個狀態,整我的都很差了。
多線程無保護的讀寫,形成了"x空懸指針"的後果,綜上,論證爲啥對shared_ptr讀寫要加鎖的緣由。
以上就是對智能指針的理解
具體參考:http://www.cnblogs.com/gqtcgq/p/7492772.html
vs2010中關於C++11 智能指針的源碼
23:37:57 2019-04-26