智能指針shared_ptr

// 智能指針會自動釋放所指向的對象。
// shared_ptr的應用場景是:程序須要在多個對象間共享數據

/* 先從應用場景入手吧,說礦工A發現了一個金礦。
 * 而後礦工A喊來了礦工B,一塊兒開採,不久後礦工A勞累過分死了。
 * 礦工B繼續開採着礦工A發現的金礦。
 * 可是礦工B不久後得了塵肺病。
 * 這時候若是礦工B喊來了礦工C,那礦工C就繼續開採這個金礦,
 * 若是礦工B至死都沒有喊anyone,那麼這個金礦再也不被任何人發現。
 *
 * 咱們來講說實現
 * 每一個礦工new一個對象,金礦new一個對象。
 * 礦工死了就delte掉,金礦再也不被發現也delte掉。
 *
 * 可是咱們有沒有可能讓最後一個礦工死時,金礦被自動delte掉?
 * 這樣的話咱們就不須要額外管理金礦對象了。
 * 有可能啊,你用共享指針啊。
 * 共享指針管理一個對象,管理一個引用計數。
 * 每次對共享指針賦值和拷貝時,引用計數就加1。
 * 當共享指針被銷燬時,引用計數就減1。
 * 這樣就變成多個礦工間共享金礦數據了。
 *
 * 下面咱們來講說引用計數遞增的狀況
 * 1 用一個shared_ptr初始化另外一個shared_ptr,確定調用拷貝構造函數嘍
 * 2 用一個shared_ptr賦值另外一個shared_ptr,確定調用賦值函數嘍
 * 3 將shared_ptr做爲參數傳遞給一個函數,這個也會調用拷貝構造函數
 * 4 將shared_ptr做爲函數的返回值,這個也會調用拷貝構造函數
 *
 * 下面咱們來講說引用計數遞減的狀況
 * 1 shared_ptr被銷燬,參數出棧是被銷燬的一種狀況
 * 2 給shared_ptr從新賦值
 *
 * 一旦一個shared_ptr的引用計數變爲0,它就會自動釋放所管理的對象。
 */

#include <iostream>
#include <memory>

using namespace std;

struct Gold
{
    ~Gold() {total = -1;}
    int total{20};

    Gold &operator--()
    {
        --total;
        return *this;
    }

    const Gold operator--(int)
    {
        Gold tmp = *this;
        --(*this);
        return  Gold(tmp);
    }
};

class Miner
{
public:
    Miner() : gold(make_shared<Gold>()) {}

    Miner(const Miner &miner)
    {
        gold = miner.gold;
    }

    void dig()
    {
        (*gold)--;
    }

    Gold *base()
    {
        return gold.get();
    }

private:
    shared_ptr<Gold> gold;
};

int main(int argc, char *argv[])
{
    auto miner1 = new Miner;
    auto miner2 = new Miner(*miner1);

    // 代碼執行到這裏
    // @表示地址 usecount是引用計數
    // miner1的gold @0x605f40
    // miner2的gold @0x605f40
    // shared_ptr的usecount是2
    // 可見miner1和miner2的gold指向同一個對象
    // 引用計數正確

    auto gold = miner2->base();

    // 代碼執行到這裏
    // gold @0x605f40

    miner1->dig();
    cout << gold->total << endl;
    miner2->dig();
    cout << gold->total << endl;
    miner1->dig();
    cout << gold->total << endl;

    delete miner1;

    // 代碼執行到這裏
    // miner1的gold (null)
    // miner2的gold @0x605f40
    // shared_ptr的usecount是1
    // 引用計數正確

    miner2->dig();
    cout << gold->total << endl;

    delete miner2;

    // 代碼執行到這裏
    // miner1的gold (null)
    // miner2的gold @0x605f20
    // miner2管理的對象(@0x605f40) 已被銷燬
    // 調用了Gold的析構函數
    // gold->totle值爲-1
    // 至於miner2的gold @0x605f20 ??
    // 管它呢,反正已引用不到

    cout << gold->total << endl;

    int *p2;
    {
        auto p1 = make_shared<int>(5);
        p2 = p1.get();

        // 代碼執行到這裏
        // p1 @0x605f60
        // usecount是1
        // p2 指向@0x605f60
    }

    // 代碼執行到這裏
    // 代碼塊出棧了,p1被銷燬
    // usecount變爲0,因此p1管理的對象也被銷燬了
    // ***這是爲何不建議用get的緣由
    // 雖然能夠正確輸出p2所指向的對象,可是這是不肯定的
    // p2就是所謂的野指針了
    cout << *p2 << endl;

    shared_ptr<int> p4;
    {
        auto p3 = make_shared<int>(5);
        p4 = p3;

        // 代碼執行到這裏
        // p3 @0x605f60
        // p4 @0x605f60
        // usecount是2
    }

    // 代碼執行到這裏
    // 代碼塊出棧了,p3被銷燬
    // usecount變爲1,p3並未銷燬所管理的對象
    // p4所管理的對象能夠正確輸出
    cout << *p4 << endl;

    return 0;
}
相關文章
相關標籤/搜索