【C++智能指针】简易shared_ptr实现(手撕shared_ptr)

关于什么是shared_ptr这里就不赘述了,废话少说直接开始!

目录

一、类大纲                

二、构造函数和析构函数

1.利用指针构造

2.拷贝构造

3.析构函数

三、功能函数

1.release()

2.addCount()

3.showCount()

四、运算符重载 

1.赋值(=)重载

2.指针解引用(*)重载

3.指针访问(->)重载

五、测试


一、类大纲     

        智能指针本质就是把指针封装起来,因此成员中必然有那个被封装的指针。因为是任意类型的指针,所以使用模板类。

        shared_ptr需要对指向每个对象的shared_ptr计数,因此选择使用一个int指针来作为这个计数变量。(指向同一对象的shared_ptr们要共享,但是指向不同对象的不能共享,因此不能选用静态变量来完成这个工作)

        基于同上的原因,使用一个mutex指针来当做指向同一对象的shared_ptr们的锁。这个锁的存在就是为了保证上面的int*的安全,所以它和int*是成对出现的。

template <class T>
class mySharedPtr
{
private:

    T* _ptr;             //被封装的指针
    int* _pcount;        //当shared_ptr指向同一对象时共享的那个计数
    mutex* _pmtx;        //当shared_ptr指向同一对象时共享的锁

public:

    //构造函数
    mySharedPtr(T* ptr);                                     //构造函数
    mySharedPtr(const mySharedPtr<T>& sp)                    //拷贝构造函数
    
    //功能函数
    void release();                                          //解除绑定
    void addCount();                                         //增加计数
    void showCount();                                        //显示计数(非必须)

    //运算符重载
    mySharedPtr& operator=(const mySharedPtr<T> &sp);        //=   赋值重载
    T& operator*();                                          //*   指针解引用重载
    T* operator->();                                         //->  指针访问重载

    //析构函数
    ~mySharedPtr();
};

二、构造函数和析构函数

1.利用指针构造

以一个普通指针为基构造需要new出计数变量和锁。

(这里引出一个内存的问题:这里new出来的int和mutex作用域是怎样的?如果new它的那个shared_ptr被析构了会发生什么?突然想到new出来的当然是在堆上啊……)

//此处是类内实现的写法
mySharedPtr(T* ptr)	
        :_ptr(ptr)
        ,_pcount(new int(1))
        ,_pmtx(new mutex)
{}
2.拷贝构造

以另一个shared_ptr为基构造。赋值另一个的各个变量并且增加计数。

(这里又出现一个权限的问题。按理说_ptr/_pcount/_pmutex这三个变量都是私有的,为什么可以直接使用?答:私有的定义是同一类哪怕不是同一个对象都可以互相直接访问,但是别的类不可以。测试过了是这样的。)

//此处是类内实现的写法
mySharedPtr(const mySharedPtr<T>& sp)	
    :_ptr(sp._ptr)
    ,_pcount(sp._pcount)
    ,_pmtx(sp._pmtx)
{
    addCount();            //因为和被拷贝的shared_ptr指向同一个对象,因此增加计数。
}
3.析构函数

为什么这里应当release()?假如我们定义了一个shared_ptr,现在它的作用域结束了,要析构了,如果你不release的话,别的指向同一个对象的shared_ptr的计数就不会减少,如此便造成了错误。

//此处是类内实现的代码
~mySharedPtr()
{
    release();            //析构时应当解绑
}

三、功能函数

1.release()

将此shared_ptr与目前绑定的指针解绑。如果解绑后该指针没有shared_ptr指向它,就把mutex和int delete掉。

template<class T>
void mySharedPtr<T>::release(){
    _pmtx->lock();

    bool deleteflag=false;
    //锁不能在lock时delete 
    //而在lock后不一定需要delete
    //所以一个标志位告诉解锁后需不需要delete mutx

    if(--(*_pcount)==0){
        cout<<"delete:"<<*_ptr<<endl;
        delete _ptr;
        delete _pcount;
        deleteflag=true;
    }

    _pmtx->unlock();
    if(deleteflag==true){
        delete _pmtx;
    }
}
2.addCount()

当调用这个函数时,增加计数。拷贝构造和赋值时会用到。

template<class T>
void mySharedPtr<T>::addCount(){
    _pmtx->lock();
    ++(*_pcount);
    _pmtx->unlock();
}
3.showCount()

纯调试函数,写不写不影响。

template<class T>
void mySharedPtr<T>::showCount(){
    cout<<(*_ptr)<<" "<<(*_pcount)<<endl;
}

四、运算符重载

1.赋值(=)重载

要记得防止自我赋值。返回值是一个共享指针对象的引用。

template<class T>
mySharedPtr<T>& mySharedPtr<T>::operator=(const mySharedPtr<T>& sp){
    if(_ptr!=sp._ptr){//防止自我赋值
        release();
        _ptr=sp._ptr;
        _pcount=sp._pcount;
        _pmtx=sp._pmtx;
        addCount();
    }

    return *this;
}
2.指针解引用(*)重载

用这个符号就是想对我们封装的那个指针指向的对象操作,所以直接返回就好了。返回值是封装的那个指针指向的类型的引用。

template<class T>
T& mySharedPtr<T>::operator*(){
    return *_ptr;
}
3.指针访问(->)重载

返回值是一个指针。(其实有一点不懂这里,理论上当使用->时,我们的目的是想要使用被封装的那个指针->,这里只是简单的返回了一个指针。不懂。)

template<class T>
T* mySharedPtr<T>::operator->(){
    return _ptr;
}

五、测试

int main()
{
    mySharedPtr<char> a(new char('a'));
    a.showCount();
    
    mySharedPtr<char> b(a);
    b.showCount();
    
    mySharedPtr<char> c(new char('b'));
    c.showCount();
    
    c=b;
    c.showCount();
    
    return 0;
}

结果:

符合预期。最后还有一个delete:a是因为程序结束a的shared_ptr也都析构了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值