仿照现代 C++ 智能指针实现自己的引用计数「上」

以下内容为本人的烂笔头,如需要转载,请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/XIWsV7LabToq_ehzmXj46w

首先,提个问题,何为引用计数,为什么要去实现引用计数?

智能指针最大的作用就是协助开发者管理内存,防止开发者遗漏释放或者错误释放已申请的内存空间,最大限度保证内存安全。使用智能指针可免除非常多的经典 C++ 版本带来的烦恼,比如招人恨的 double free or corruption。

据微软内部统计,C++ 应用的绝大部分问题属于内存安全问题,所以保障内存安全的措施真的非常重要。

那么,智能指针是如何保证内存安全的呢?

智能指针在初始化时会托管传入的内存空间,在后续的流程中,一旦识别到被托管的内存不再被需要的时候,自动释放这块内存。

如何识别被托管的内存是否还被需要?

通过引用计数,引用计数就是计数器,一般用整形变量表示,代表有多少个智能指针已托管该内存空间。

已托管的内存每增加绑定一个智能指针,该计数器自动加 1;反之,已托管的内存每解绑一个智能指针(比如智能指针超出生命周期,或者主动解除绑定),该计数器自动减 1。直到计数器归 0,这时被托管的内存即被判断为不再需要保留,最后解绑的智能指针负责释放该内存空间。

其中,可见该计数器被所有绑定的智能指针共享访问。

虽然标准库已经提供非常多现成的智能指针可供调用,但是其中的奥妙还是非常值得我们细细揣摩,下面就开始跟随笔者一起实现自己的引用计数帮助类,权当作略微简陋的自定义智能指针。

先定义两个类,一个是代表占用内存资源的类 Demo,另一个是就是我们自定义的智能指针类 SmartPtr

class Demo
{};

class SmartPtr
{};

资源类附加属性

由于绑定同一个内存资源的所有智能指针是共享访问该内存的,所以可以在类 Demo 中附加定义计数器 count_,计数器作为专用属性应该带有隐私属性也就是不允许随意被类外访问,所以同时应该被修饰为 private。

在类 Demo 实例化时,计数器 count_ 应该被清零,然后才能被 SmartPtr 托管。所以要求类 Demo 的所有构造函数都初始化 count_ 为 0。

class Demo
{
public:
    Demo() : count_(0) {};
private:
    unsigned int count_;
};

作为托管方的类 SmartPtr 需要有权限可以直接读写类 Demo 的私有成员 count_,所以在类 Demo 中还要声明类 SmartPtr 为友元类

class Demo
{
    // ...
private:
    // ...
    friend class SmartPtr;
};

关于代表资源的类的简单改造就介绍完了。

自定义智能指针类

智能指针类 SmartPtr 作为托管方,需要实现基本的功能接口,包括托管、解绑、访问资源成员、获取资源引用等。

托管

智能指针需要托管某个内存资源,那么就需要在内部有对应的指针指向资源。同样为了不必要的暴露,这个指针需要用 private 修饰。

class SmartPtr
{
private:
    Demo *p_;
};

在托管资源时,资源对应的引用计数器应该自增 1,由于我们定义的智能指针在资源类的内部被声明为友元类,所以智能指针绑定资源时可以直接读写资源的计数器。

智能指针如何绑定资源,或者说如何接收将要被托管的资源呢?

资源未被托管

如果资源当前没有被任何智能指针托管,那么,可以在实例化智能指针对象时,利用智能指针的构造函数接收资源指针,并保存到内部指针变量,按照上面的代码就是保存到变量 p_。

class SmartPtr
{
    // ...
public:
    SmartPtr(Demo *p) : p_(p) {
        ++ p_->count_;
    }
};

分享托管

如果资源当前正在被其它智能指针托管,那么,可以从其它的智能指针分享过来。

有两种分享的方法,一是创建一个新的智能指针来接收其它智能指针的分享,需要用到拷贝构造函数,并传入其它的智能指针的引用实例。

class SmartPtr
{
    // ...
public:
    SmartPtr(const SmartPtr &obj) : p_(obj.p_) {
        ++ p_->count_;
    }
};

二是,如果有个智能指针实例当前已经托管某个资源,但是需要重新绑定其它资源,并且当前托管的资源需要解绑,可以利用拷贝赋值运算符,右侧操作数就是其它分享资源的智能指针,左侧操作数是当前的智能指针。

解绑当前托管的资源,除了要清除原有的资源指针备份,还需要对资源的计数器减 1,并且判断计数器减 1 后是否归零,如果归零就需要释放该资源占用的内存

class SmartPtr
{
    // ...
public:
    SmartPtr& operator=(const SmartPtr& sp) {
        Demo *p = p_;
        p_ = sp.p_;
        ++ p_->count_;
        if (0 == (-- p->count_)) {
            delete p;
        }
        return *this;
    }
};

全文未结束,此为上篇,关注我查看更多内容。

最后,非常感激各位朋友的点 「赞」 和点击 「在看」,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值