智能指针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;
}

posted on 2017-02-09 18:34  ddev  阅读(499)  评论(0编辑  收藏  举报