智能指针强指针循环引用问题
先了解一下shared_ptr的特点就是「多个指针可以共享同一个对象」规则非常简单每多一个shared_ptr指向同一个对象对象的引用计数就1每销毁一个shared_ptr比如出作用域、被重新赋值对象的引用计数就-1当引用计数降到0的时候说明没有任何指针指向这个对象了shared_ptr会自动帮我们delete这个对象释放内存相当于给对象套了个引用计数器没人用了自动回收不用我们手动管理解决了忘记释放、重复释放这些问题。看起来你这个是C中shared_ptr循环引用导致内存泄漏的典型问题我给你拆解一下整个过程和原因1. 引用计数的变化过程我们一步步理初始创建shared_ptrA pa(new A())后A对象的引用计数是1shared_ptrB pb(new B())后B对象的引用计数是1交叉赋值pa-_ptrb pbpa内部持有了pb的引用B对象引用计数1 → 变成2pb-_ptra papb内部持有了pa的引用A对象引用计数1 → 变成2所以最后打印pa.use_count()和pb.use_count()结果就是你图里的2 22. 为什么会内存泄漏当main函数结束的时候首先会销毁栈上的智能指针对象pa销毁pa会让A对象的引用计数-1 → 从2变成1接着销毁栈上的pb销毁pb会让B对象的引用计数-1 → 也从2变成1此时问题来了A对象里还存着B的shared_ptr引用B对象还存着A的shared_ptr引用两个对象的引用计数都停留在1永远没法降到0因此谁都不会被释放就造成了内存泄漏♂️栈上的pa/pb销毁后堆上的A、B对象都还没死它们自己内部互相抱着对方的引用把引用计数卡在1永远降不到0所以永远不会被释放就泄漏了。3. 怎么解决这个问题把其中一个指针改成weak_ptr弱引用就好啦比如把A类里的shared_ptrB _ptrb改成weak_ptrB _ptrb或者把B里的_ptra改成weak_ptr。弱引用不会增加原对象的引用计数这样最后结束的时候引用计数会正常降到0对象就可以正常释放不会泄漏了。我们走一遍引用计数变化你就懂了1.初始创建 shared_ptrA pa(new A()) → A计数 1 shared_ptrB pb(new B()) → B计数 12.交叉赋值后 pa-_ptrb pbweak_ptr不会增加引用计数所以B计数还是1 pb-_ptra pa这个还是shared_ptr所以A计数 1 → A变成23.main结束销毁栈上对象销毁顺序pb→pa·先销毁pbpb自己析构 → B计数 -1 → 从1变成0 → B对象直接被释放了·B对象释放的时候B里面的shared_ptrAptra也跟着被销毁了 → 这个ptra会给A计数再减1 → A从2变成1·接下来销毁papa自己析构 → A计数再减1 → 从1变成0 → A对象也顺利被释放。1.「定义对象的时候用强智能指针shared_ptr」这里说的「定义对象」其实指的是负责持有对象所有权、需要保证对象存活的指针要用shared_ptr。比如我们例子里外部main里的pa/pb还有交叉引用里留的那个成员指针都是负责保活对象的用shared_ptr才能保证对象在用的时候不会提前被释放。2.「引用对象的地方使用弱智能指针weak_ptr」这里说的「引用对象」就是指你只是需要访问/指向另一个对象不需要负责维持它的生命周期这种情况就用weak_ptr。比如我们循环引用的场景里反向指向的那个指针就属于这种“只引用不持有”用weak_ptr不会增加引用计数刚好打破循环解决内存泄漏。1. 为什么多线程里需要侦测对象是否存活举个很常见的场景我们有一个工作线程持有一个对象的指针准备后面异步处理它但这个对象其实可能在主线程里已经被提前释放了如果我们直接访问就会访问野指针直接崩溃。2. 怎么用weak_ptr解决这个问题weak_ptr天生就适合做这件事我们让工作线程持有weak_ptr要访问的时候先调用lock()如果对象已经被释放lock()会返回空的shared_ptr我们直接跳过访问就好不会崩如果对象还活着lock()会返回一个有效的shared_ptr保证我们访问过程中对象不会被释放安全可靠