C++日記——Day29:shared_ptr常用操作、計數、自定義刪除器等等

shared_ptr引用計數的增加和減少

共享式:引用計數,每個shared_ptr的拷貝都指向相同的內存(對象),只有最後一個指向該對象的shared_ptr指針在不需要在指向該對象時,shared_ptr纔會去析構所指向的對象。

 

引用計數的增加

每個shared_ptr都會記錄有多少個其他的shared_ptr指向相同的對象;

auto p6 = make_shared<int>(100);
auto p7(p6); //智能指針定義時初始化,p7和p6指向了相同的對象。


shared_ptr<int> func(shared_ptr<int> &tmp){
    return tmp;
}

auto p8 = func(p7);

在如下情況下,所有指向這個對象的share_ptr引用計數都會增加1;

1、像上邊這樣用p6來初始化p7這個智能指針;

2、把智能指針當做實參向函數中傳遞;(如果參數爲智能指針的引用,則智能指針的計數不會增加)

3、作爲函數的返回值,(如果沒有變量來接這個返回的臨時智能指針,則臨時智能指針生命週期到了以後計數自動減少1)

 

引用計數的減少

p8 = make_shared<int>(200); //p8指向新對象,計數爲1,p6、p7指向對象計數恢復爲2;
p7 = make_shared<int>(300); //p7指向新對象,計數爲1,p6指向對象的計數恢復爲1;

1、shared_ptr賦予新值,讓該shared_ptr指向一個新對象。

2、局部的shared_ptr離開其作用域。

3、當一個shared_ptr引用計數從1變成0,則它會自動釋放自己所管理的對象。

auto p9 = make_shared<int>(100);
auto p10 = make_shared<int>(100);
p9 = p10; //給p9賦值會讓p9指向p10所指向的對象,該對象引用計數變成2,而原來p9所指向的 
          //對象引用計數會從1變成0,從而導致p9所指向的對象被釋放

 

 

shared_ptr常用操作

use_count():返回多少個智能指針指向某個對象,主要用於調試。

shared_ptr<int> myp(new int(100));
int count = myp.use_count(); // 1
shared_ptr<int> myp2(myp);
int count = myp.use_count(); // 2
shared_ptr<int> myp3;
myp3 = myp2;
int count = myp3.use_count(); // 3
int count = myp.use_count(); // 3

 

unique():是否該智能指針獨佔某個指向的對象,也就是若只有一個智能指針指向某個對象,則unique()返回true。(爲空時也不屬於獨享)

 

reset():恢復(復位/重置)的意思

1、若reset不帶參數時

若pi是唯一指向該對象的指針,那麼釋放pi所指向的對象,並將pi置空

若pi不是唯一指向該對象的指針,那麼不釋放pi所指向的對象,但指向該對象的引用計數會減少1,同時將pi置空

shared_ptr<int> pi(new int(100));
pi.reset();
if (pi == nullptr)
{
    cout << "pi被置空" << endl;
}
shared_ptr<int> pi(new int(100));
auto pi2(pi);
pi.reset(); //pi所指向的對象的引用計數減少1

2、reset帶參數時

若pi是唯一指向該對象的指針,則釋放pi指向的對象,讓pi指向新對象。

若pi不是唯一指向該對象的指針,則不釋放pi指向的對象,但指向該對象的引用計數會減少1,同時讓pi指向新對象。

 

空指針也可以通過reset重新初始化。

shared_ptr<int> p;
p.reset(new int(10));

 

*解引用:獲取p指向的對象。

shared_ptr<int> pi(new int(123));
cout << *pi << endl;

 

get():考慮到有些函數的參數需要的是一個內置裸指針,而不是智能指針。

p.get():返回p中保存的裸指針,小心使用,如果智能指針釋放了所指向的對象,那麼這個返回的裸指針也就無效了。

shared_ptr<int> mypi(new int(222));
int *p = mypi.get();
*p = 123;  //此處的p屬於智能指針,不要delete

 

swap():交換兩個智能指針所指向的對象。

shared_ptr<string> ps1(new string("1111111"));
shared_ptr<string> ps2(new string("2222222"));
std::swap(ps1, ps2); //交換ps1指向222222222
ps1.swap(ps2);    //在交換ps1指向11111111

 

=nullptr

1、將所指向的對象的引用計數減1,若引用計數變爲0,則釋放智能指針所指向的對象。

2、智能指針將被置空。

 

智能指針名字作爲判斷條件

shared_ptr<string> ps1(new string("1111"));
if(ps1)
{
    cout << "ps1指向一個對象" << endl;
}

 

指定刪除器以及數據問題

1、指定刪除器:一定時機幫我們刪除所指向的對象;delete:將delete運算符號作爲默認的資源析構方式

我們可以指定自己的刪除器取代系統提供的默認刪除器。當智能指針需要刪除所指向的對象時,編譯器就會調用我們自己的刪除器來刪除該對象

shared_ptr指定刪除器的方法比較簡單,一般只需要在參數中添加具體的刪除器函數名即可

void mydelete(int *p) //這個刪除器用於刪除整型指針,當智能指針引用計數爲0,就會自動調 
                      //用該刪除器來刪除對象
{
    //寫一些日誌
    delete p;  //既然你自己提供了刪除器你就有義務自己來刪除所指向的對象(引用計數爲0 
               //時)
}


shared_ptr<int> p(new int(123), mydelete);
shared_ptr<int> p2(p);  //兩個引用計數
p2.reset();  //還剩1個引用計數
p.reset();  //因爲此時只有1個引用計數,減1後爲0,所以要釋放所指向的對象,調用mydelete 
            //刪除器,同時p置空
shared_ptr<int> p(new int(123), [](int *p){ 
                                            delete p; 
                                          });

有些情況,默認刪除器處理不了(比如用shared_ptr管理動態數組),需要我們自己指定刪除器;

class A {
public:
    A() {};
    ~A() {};
}

shared_ptr<A> p(new A[10]); //釋放時會報內存泄漏異常

shared_ptr<A> p(new A[10], [](A *pa){ 
                                            delete [] pa; 
                                         });

可以用default_delete來做刪除器,這個是標準庫裏的模板類。

class A {
public:
    A() {};
    ~A() {};
}


shared_ptr<A> p(new A[10], std::default_delete<A[]>());

 

C++新規則 還有一種簡單寫法

shared_ptr<A[]> p(new A[10]);

shared_ptr<int[]> p(new int[10]);
p[0] = 12;
p[1] = 15;

這種情況不需要再指定刪除器

 

函數模板封裝shared_ptr

template<typename T>
shared_ptr<T> make_shared_array(size_t size)
{
    return shared_ptr<T>(new T[size], default_delete<T[]>());
}


shared_ptr<A> parray = make_shared_array<A>(15);

 

指定刪除器額外說明

就算是兩個shared_ptr指定了不同的刪除器,只要他們所指向的對象類型相同,那麼這兩個shared_ptr也是屬於同一類型。

auto lambda1 = [](int *p)
{
    delete p;
};

auto lambda2 = [](int *p)
{
    delete p;
};


shared_ptr<int> p1(new int(100), lambda1);
shared_ptr<int> p1(new int(100), lambda2);
p2 = p1; //p2會先調用lambda2把自己所指向的對象釋放,然後指向p1所指向的對象。p1所指向 
         //的對象引用計數爲2,整個main執行完成後還會調用lambda1來釋放p1,p2共同指向的 
         //對象

類型相同就代表可以放到元素類型爲該對象的容器裏來;

vector<shared_ptr<int>> pvec{p1, p2};

make_shared是提倡的生成share_ptr方法,但是使用這種方法我們無法指定自己的刪除器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章