C++ 智能指針【學習筆記】

  • 頭文件memory

shared_ptr類

  • 允許多個指針指向同一個對象;

創建和初始化

shared_ptr<string> p1;
shared_ptr<int> p2 = make_shared<int>(42);
shared_ptr<string> p3 = make_shared<string>(10,'9');
shared_ptr<int> p4 = make_shared<int>();//值初始化
auto p5 = make_shared<vector<string>>();

拷貝和賦值

  • 拷貝:遞增計數器
auto p = make_shared<int>(42);
auto q(p);
  • 賦值:遞減等號左邊的計數器,遞增等號右邊的計數器
auto r = make_shared<int>(42);
r = q;//遞減r指向對象的引用計數,遞增q指向對象的引用計數
  • 當指向一個對象的最後一個shared_ptr被銷燬時,shared_ptr會自動銷燬指向的對象,並釋放相關聯的內存。

其他操作

  • *p

  • p.get():返回內置指針;

  • swap(p,q):交換指針;

  • p.use_count():返回與p共享對象的智能指針數量;

  • p.unique():如果use_count返回1,則返回true,否則返回false;

  • p.reset():若p是唯一指向其對象的shared_ptr,則釋放此對象;如果不是,就把p置空;

  • p.reset(q):若p是唯一指向其對象的shared_ptr,則釋放此對象;令p指向q;

    shared_ptr<int> p(new int(1024));//正確,使用普通指針初始化智能指針
    p = new int(2048);               //錯誤,普通指針不能轉化爲智能指針
    p.reset(new int(2048));//正確,p指向一個新對象,並釋放原有對象(因爲p是唯一指向原對象的智能指針)
    
    shared_ptr<int> p1(new int(1024));
    shared_ptr<int> p2(p1);
    p1.reset(new int(2048));//此時p1指向2048,而p2指向1024
    
  • reset()經常與unique()一起使用,來控制多個shared_ptr共享的對象:

    if(!p.unique())
    	p.reset(new string(*p)); //如果p不是獨有該對象,則先製作一份拷貝,使得p指向拷貝的對象
    *p+=newVal; //在拷貝對象上進行操作,不會影響其他共享的智能指針
    

unique_ptr類

  • 某個時刻只能有一個unique_ptr指向一個給定對象;

不支持拷貝和賦值!

unique_ptr<int> p1=new int(1024);  //錯誤!!不能轉換
unique_ptr<int> p1(new int(1024)); //正確!!
unique_ptr<int> p2(p1);            //錯誤!!不能拷貝
unique_ptr<int> p2=p1;             //錯誤!!不能賦值
  • 函數的實參傳遞和返回值也是拷貝,但是由於返回值拷貝之後,原unique_ptr會被銷燬,因此返回一個unique_ptr是正確的,但是實參傳遞不支持拷貝。

操作:

  • up.get():返回內置指針;
  • up.release()up置空,返回普通指針但是不釋放up指向的對象
  • up.reset()釋放up指向的對象;up置空
  • up.reset(q)釋放up指向的對象(因爲up是唯一的指針);令up指向q(新對象)
  • 通過調用release()reset()實現“拷貝和賦值”:
unique_ptr<int> p1(new int(1024));//                            p1->1024
unique_ptr<int> p2(p1.release()); //將對象的所有權從p1轉到p2;      p2->1024,p1->NULL
unique_ptr<int> p3(new int(2048));                           // p3->2048
p2.reset(p3.release());//釋放p2指向的對象,然後p2指向p3             p2->2048, p3->NULL
p2.reset(new int(4096));                                     // p2->4096

weak_ptr類

  • 指向一個由shared_ptr管理的對象,一旦最後一個指向對象的shared_ptr被銷燬,對象就會被釋放,即使有weak_ptr指向該對象。

初始化

  • 只能用shared_ptr來初始化weak_ptr:
auto p=make_shared<int>(42);
weak_ptr<int> wp(p); //p指向的對象的計數不會增加

拷貝和賦值

shared_ptr<int> p=make_shared<int>(42);
weak_ptr<int> wp=p; //可以將shared_ptr轉化爲weak_ptr;

操作:

  • wp.reset():wp置空;

  • wp.use_count():返回與wp共享的shared_ptr的數量;

  • wp.expired():如果use_count()爲0,返回true,否則返回false;

  • wp.lock():如果expired()爲true,返回空shared_ptr指針,否則返回一個指向wp對象的shared_ptr;

  • 由於weak_ptr指向的對象可能不存在,因此不能使用weak_ptr直接訪問對象:

    if(shared_ptr<int> np=wp.lock()){*np++}
    

weak_ptr的作用

  • weak_ptr 設計的目的是爲配合 shared_ptr 而引入的一種智能指針來協助 shared_ptr 工作, 它只可以從一個 shared_ptr 或另一個 weak_ptr 對象構造, 它的構造和析構不會引起引用記數的增加或減少。weak_ptr是用來解決shared_ptr相互引用時的死鎖問題,如果說兩個shared_ptr相互引用,那麼這兩個指針的引用計數永遠不可能下降爲0,資源永遠不會釋放。

    class B;
    class A{
    public:
    	shared_ptr<B> pb_;
    	~A(){cout<<"A delete\n";}
    };
    
    class B{
    public:
    	shared_ptr<A> pa_;
    	~B(){cout<<"B delete\n";}
    };
    
    void fun(){
    	shared_ptr<B> pb(new B());
    	shared_ptr<A> pa(new A());
    	pb->pa_ = pa;
    	pa->pb_ = pb;
    	cout<<pb.use_count()<<endl;
    	cout<<pa.use_count()<<endl;
    }
    
    int main(){
    	fun();
    	return 0;
    }
    

在這裏插入圖片描述

  • 這種情況下,指向對象A和對象 B指針計數都是2,當退出函數fun()之後,引用計數分別減1,這樣對象A和對象B的引用計數都變成1,無法釋放內存;
  • 如果將其中一個shared_ptr改爲weak_ptr,由於weak_ptr不改變對象的引用計數,所以fun()執行時的引用計數分別爲2和1,退出fun()之後,分別遞減成1和0,導致B對象被釋放,釋放B的同時也會讓A的計數減1,這樣AB可以全部被釋放;
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章