// 智能指针会自动释放所指向的对象。 // 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; }