手撕一个简易的shared_ptr

共享指针shared_ptr是RALL机制的一个典型实现,主要功能是管理动态创建对象的销毁,从而帮助彻底消除内存泄漏和悬空指针(野指针)的问题,并且通过引用计数的方式实现了可以多个共享指针可以指向同一个对象。

由于最近经常看到这个问题,查阅了相关文献,自己来尝试手写一个简易的shared_ptr,有错误还望指出。

具体地,一个简易的shared_ptr应该包括以下几个部分:

1、构造函数与析构函数,动态创建及销毁对象。

2、重载拷贝构造函数,个人理解是为了使动态指针能够像普通指针一样进行值传递。

3、重载=赋值运算符,->运算符以及*运算符使其更像指针。

最主要部分还是要维护创建的指针以及引用计数。

首先看一下整体的框架

tmplate<class T>
class new_shared_ptr {
pubilc:
    new_shared_ptr();

    new_shared_ptr(const new_shared_ptr& p);

    ~new_shared_ptr();

    new_shared_ptr& operator=(const new_shared_ptr<T>& obj);
    
    int operator->();
    
    int operator*();

private:
    T* ptr;
    int* ref_count;
    mutex *m_pMutex;
}

首先实现一下构造函数与拷贝构造函数,主要是对指针、引用计数以及锁进行初始化,注意引用计数改变时需要加锁,保证线程安全

//构造函数
new_shared_ptr(): ptr(new T), ref_count(new int(1)), mutex(new mutex) {}

//拷贝构造函数
new_shared_ptr(const new_shared_ptr& p) {
    //先对当前对象进行初始化
    ptr = p.ptr;
    ref_count = p.ref_count;
    mutex = p.mutex;
    //判断拷贝进来的指针是否指向空,不指向空表明当前地址多了一个指向,引用计数加一
    if (p != nullptr) {
        m_pMutex->lock();
        (*ref_count)++;
        m_pMutex->unlock();
    }
}

重载=赋值运算符,->运算符以及*运算符。

new_shared_ptr& operator=(const new_shared_ptr<T>& obj) {
    //检查自赋值
    if (obj.ptr == ptr) return *this;
    //防止空指针指向别处
    if (ptr) {
        //自身指向即将改变,判断原先指向的地址是否销毁
        m_pMutex->lock();
        (*ref_count)--;
        m_pMutex->unlock();
        if (ref_count == 0) {
            delete ptr;
            delete ref_count;
            delete m_pMutex;
        }
        ptr = obj.ptr;
        ref_count = obj.ref_count;
        m_pMutex = obj.m_pMutex;
        m_pMutex->lock();
        (*ref_count)++;
        m_pMutex->unlock();
    }
    return *this;
}

T* operator->() return ptr;
T& operator*() return *ptr;

最后是析构函数,同样是判断引用计数的值,如果不为零则只减一,为0才进行销毁

~new_shared_ptr() {
    if (!ptr || (*ref_count == 1)) {
        delete ptr;
        delete m_pMutex;
        delete ref_count;
    } else {
        m_pMutex->lock();
        (*ref_count)++;
        m_pMutex->unlock();
    }
}

可以再写一个获取当前引用计数的接口

int get_count() {
    return *ref_count;
}

至此一个简单的线程安全的共享指针就写完了,当然实际上的共享指针还应该包括移动构造函数等,这里就不一一列出,再说一个我遇到的问题:

为什么引用计数要声明为指针?

        主要是为了保证所有指向该内存的指针共享同一份数据,每次指针拷贝的时候,实际上是拷贝的指针,该指针同样指向的是相同的地址,所以实现了一个同步的功能。为了防止出现两个指针同时对某一块内存操作,每次对引用计数的操作还需要加锁来保证线程安全。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值