智能指针循环引用——你真的懂了吗?

相信不少同学都在面试中都被问到过c++智能指针的问题,接踵而至的必定是循环引用了,而我每次的答案都是一招鲜:因为它们都在互相等待对方先释放,所以造成内存泄漏。面试官很满意,我也很满意。

但是为啥要等到对方先释放?在我内心也曾有个问号。。

智能指针起源

所谓人类进步的阶梯就是懒,曾几何时,就有人想,我把new 运算符返回的指针p交给一个对象“托管”,而不用操心在哪里去释放这个指针p,由这个托管者自动的在合适的时机进行指针p的释放,这样也不怕自己忘记释放指针p了。而且,我这边操作托管者,要跟操作原来的指针一毛一样,这样才方便。即假设托管指针p的对象叫做shared_ptr,那么

*shared_ptr 所指,便是 *p 所向!

第一章 auto_ptr

最早的智能指针类,实现了*运算符。你用它实例化出来一个类对象,用起来就好像指针一样。对象的特性自然是在作用域结束时,自动调用析构函数实现自我销毁。如此一来,攻城狮们便可不再操心何时释放指针,而可以随心所欲的摸鱼啦。

但其有个致命缺陷:因为其缺省的复制和赋值构造函数,带来的同一个对象被多个智能指针托管,释放的时候便混乱异常,一不小心就重复释放了,使得用户使用起来战战兢兢,老早就被淘汰了。

第二章 unique_ptr

为了规避auto_ptr可以随意复制和赋值构造的缺陷,就推出了unique_ptr, 其实就是给auto_ptr简单换了个名字,并禁用掉其默认的复制和赋值构造函数,好嘛,大家都别用了<(`^′)>,不写代码,就不会有bug!

第三章 shared_ptr

只有一个unique_ptr,对于省吃俭用的高级攻城狮们来说自然是不够的,于是就有人站了出来喊了一声:我们要搞共享经济——砰!基于引用计数的shared_ptr横空出世了,每多一个人持有这个对象,引用计数就加一;每少一个人持有这个对象,引用计数就减一。引用计数为零时,才做真正的销毁操作。

很快,梦魇悄然而至,“我的shared_ptr怎么没有释放?”——一位焦头烂额的格子少年路过。。

起初,没有人在意这场灾难,这不过是一个指针的丢失、一个bug,一个服务器的宕机,直到这场灾难和每个人息息相关......

"你看这个人写的代码,它好像一坨狗屎"

#include <iostream>

class B;
class A {
public:
    A() {
        std::cout << "A" << std::endl;
    }
    ~A() {
        std::cout << "~A" << std::endl;
    }
public:
	std::shared_ptr<B> ptr;
};

class B {
public:
    B() {
        std::cout << "B" << std::endl;
    }
    ~B() {
        std::cout << "~B" << std::endl;
    }
public:
	std::shared_ptr<A> ptr;
};

void fun() {
	std::shared_ptr<A> pa(new A());
	std::shared_ptr<B> pb(new B());
	pa->ptr = pb;
	pb->ptr = pa;
}

int main()
{
	fun();
	return 0;
}

运行结果我不敢看: (*/ω\*)

纳尼?A和B的析构函数都没有被调用,妥妥的内存泄漏了! 

事后,据某位亲身经历这次事件的大牛回忆说:“喔,当时的内存布局是这个样子的!”

“对象A同时被pa和对象B中的ptr两个智能指针托管,所以引用计数为2;对象B同时被pb和对象
A中的ptr两个智能指针托管,所以引用计数也为2。那么当fun函数执行完,栈对象pb、pa依次开始执行这样的析构函数:”

// 大牛随手写的析构函数伪代码
// Copyright 2022 DaNiu. All rights reserved.

if (--ref_cnt == 0) delete obj;

“紧接着内存布局就变成了这样:”

“pa和pb已经销毁了,然而对象A和B,已经迷失在这浩瀚内存中,亘古难灭。。。吾称之为循环引用!”

路人震惊:“嘶!随着这样的迷失越来越多,这片天地再也无人可搞对象!!!”

゜゜(´O`) ゜゜。:“不要啊!我还没有搞过对象呢。”

......

庆幸的是,不就之后,有人就在shared_ptr出世的那方世界的一个不起眼的角落里,发现了一个小家伙,伴shared_ptr而生。

终章 weak_ptr

为了解决shared_ptr在在循环引用中存在的资源泄漏问题,weak_ptr在这种场景下应用而生,weak_ptr指向的智能指针对象,其引用计数不会加一,也就不会存在无法释放的问题了。

解决的方法就是,把A和B其中的一个ptr改成weak_ptr。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fireplusplus

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值