C++实现智能指针(shared_ptr和unique_ptr)与删除器

C++标准库中又两种智能指针分别为:shared_ptr和unique_ptr

shared_ptr和unique_ptr之间最大的区别就是:shared_ptr允许共享指针,而unique_ptr则独占指针。

还有一个差异就是两种指针的删除器的差异。对于shared_ptr来说,删除器是可以重载的,所以其类型是在运行时绑定。而unique_ptr的删除器不能重载,且是unique_ptr类的一部分,在其编译时绑定。

根据以上这两点,我们对于实现也要采取不同的策略。

首先是shared_ptr的实现。

根据之前提到的,shared_ptr允许共享指针,那么我们就要做一个指针的引用计数,思路是使用new表达式来统计引用计数,每有一个指针被赋值则引用计数加一,相反,当指针被delete时引用计数减一。

先贴代码,如下:

#include <iostream>
#include <functional>

using namespace std;

class deleter{
public:
    deleter()= default;
    template<typename T>
    void operator()(T* p)const{
        delete p;
        p=nullptr;
        cout<<"new deleter"<<endl;
    }
};


template<typename T>
class New_shared_ptr{
public:
    New_shared_ptr()=default;
    explicit New_shared_ptr(T *p,function<void(T*)> d=deleter()):ptr(p),use(new size_t(1)),del(d){}    //explicit
    New_shared_ptr(const New_shared_ptr&p):ptr(p.ptr),use(p.use),del(p.del){++*use;}   //copy
    New_shared_ptr(New_shared_ptr&&p) noexcept :ptr(p.ptr),use(p.use),del(std::move(p.del)){     //move
        p.ptr=nullptr;
        p.use=nullptr;
    }
    T &operator*();
    T* operator->();
    New_shared_ptr &operator=(const New_shared_ptr&);
    New_shared_ptr &operator=(New_shared_ptr &&) noexcept ;
    operator bool() const { return ptr ? true : false;}

    T* get(New_shared_ptr&);
    bool unique();
    size_t use_count();
    void reset(){decrement_n_destroy();}        //一共3个重载版本
    void reset(T *p){
        if(ptr!=p){
            decrement_n_destroy();
            ptr=p;
            use=new size_t(1);
        }
    }
    void reset(T *p,const function<void(T*)> &d){
        reset(p);
        del=d;
    }
    ~New_shared_ptr();
private:
    T* ptr= nullptr;
    size_t *use=new size_t(0);    //引用计数
    function<void(T*p)> del{deleter()};
    void decrement_n_destroy();
};

template <typename T>
T* New_shared_ptr<T>::operator->() {
    return & this->operator *();
}
template <typename T>
T& New_shared_ptr<T>::operator*() {
    return *ptr;
}
template <typename T>
New_shared_ptr<T>&  New_shared_ptr<T>::operator=(const New_shared_ptr &rhs) {
    ++*rhs.use;
    if(--*use==0){
        delete use;
    }
    use=rhs.use;
    del=rhs.del;
    return *this;
}
template <typename  T>
New_shared_ptr<T>&
New_shared_ptr<T>::operator=(New_shared_ptr &&p) noexcept{
    decrement_n_destroy();
    std::swap(this->use,p.use);
    std::swap(this->ptr,p.ptr);
    std::swap(this->del,p.del);
}

template<typename  T>
T* New_shared_ptr<T>::get(New_shared_ptr&){
    return ptr;
}
template <typename T>
size_t New_shared_ptr<T>::use_count(){
    return *use;
}
template <typename T>
bool New_shared_ptr<T>::unique() {
    return *use==1;
}
template <typename T>
New_shared_ptr<T>::~New_shared_ptr() {
    decrement_n_destroy();
}
template <typename T>
inline void
New_shared_ptr<T>::decrement_n_destroy()
{
    if(ptr)
    {
        if (--*use == 0)
        {
            delete use;
            del(ptr);
        }
        use = nullptr;
        ptr = nullptr;
    }
}


deleter是我们定义的删除器,也就是封装了指针的类,在这里用到了function模版类型。用来保存可调用对象,也就是保存我们自己定义可以用来重载的删除器,其形式可以是函数指针,函数,lambda表达式,bind函数以及重载了函数调用运算符的类。

其他部分的实现比较简单,主要注意的就是:要注意赋值和移动运算符中可能会出现自赋值的情况。

下面是unique_ptr的实现

首先说想法,unique_ptr是独占指针的,所以在实现的时候是不需要引用计数的,其次unique_ptr没有赋值操作(但是有移动操作!)

其次,unique_ptr的删除器是类的成员(可以自己指定)

代码如下:

#include <string>
#include <iostream>
#include "deleter.h"

using namespace std;
template<typename,typename>
class New_unique_ptr;

template<typename T,typename D>
void swap(New_unique_ptr<T,D> &p1,New_unique_ptr<T,D> &p2){
    std::swap(p1.ptr,p2.ptr);
    std::swap(p1.del,p2.del);
}


template<typename T,class D=deleter>
class New_unique_ptr{
    friend void swap<T,D>(New_unique_ptr<T,D> &p1,New_unique_ptr<T,D> &p2);
public:
    New_unique_ptr()=default;
    explicit New_unique_ptr(T*p,D d1=deleter()):ptr(p),del(d1){}
    //unique_ptr没有赋值操作
    New_unique_ptr(const New_unique_ptr& up)=delete;
    New_unique_ptr&operator=(const New_unique_ptr&)=delete;
    //移动操作要将指向的内容一起移动
    New_unique_ptr(New_unique_ptr&& up)noexcept :ptr(up.ptr),del(up.del){up.ptr= nullptr;}
    New_unique_ptr& operator =(New_unique_ptr&& rhs) noexcept;
    New_unique_ptr&operator=(nullptr_t p)noexcept;
    //重载操作
    T& operator*(){ return *ptr; }
    T* operator->(){ return &this->operator*();}


    ~New_unique_ptr();

    T* get() const noexcept { return ptr;}
    T* release();
    void reset();
    void reset(T* p);

private:
    T* ptr= nullptr;
    D del=deleter();
};


template<typename T,class D>
New_unique_ptr<T,D>& New_unique_ptr<T,D>::operator=(New_unique_ptr&& rhs) noexcept{
    if(this->ptr!=rhs.ptr){
        del(ptr);
        ptr= nullptr;
        ::swap(*this,rhs);
    }
    return *this;
};
template<typename T,class D>
New_unique_ptr<T,D>& New_unique_ptr<T,D>::operator=(nullptr_t p)noexcept{
    if(p==nullptr){
        del(ptr);
        ptr=nullptr;
    }
    return *this;
};

template<typename T,class D>
T * New_unique_ptr<T,D>::release(){
    T *p=ptr;
    ptr=nullptr;
    return p;
};

template<typename T,class D>
void New_unique_ptr<T,D>::reset(){
    del(ptr);
    ptr=nullptr;
};

template<typename T,class D>
void New_unique_ptr<T,D>::reset(T* p){
    del(ptr);
    ptr=p;
};

template<typename T,class D>
New_unique_ptr<T,D>::~New_unique_ptr(){
    del(ptr);
};
删除器的实现如下:

#include <iostream>
using namespace std;

class deleter{
public:
    deleter(){}
    template<typename T>
    void operator()(T* p){
        delete p;
        p=nullptr;
        cout<<"already delete"<<endl;
    }
};

说下自己的收获吧,造轮子对于刚学习完入门C++的人来说很重要,打好基础才是最重要的。实现完智能指针,自己对于模版,类,重载又有类新的认识。

还有一个我觉得比较容易忽视的一个点就是

对于operator->()的理解,成员访问运算符返回值是指针类型。对于它的原理大家可以再重新看一遍,可能会有不一样的收获哦。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是C++三种智能指针实现原理和区别。 首先,我们需要明确一下什么是智能指针智能指针是一种C++的类,它的行为类似于指针,但是它具有一些额外的功能,比如自动内存管理。智能指针能够自动释放所管理的对象,从而避免内存泄漏和野指针的问题。 下面我们分别介绍shared_ptrunique_ptr、weak_ptr三种智能指针实现原理和区别。 ## shared_ptr shared_ptr是一种引用计数智能指针,它的实现原理是通过使用引用计数来跟踪有多少个shared_ptr对象指向同一个对象。每当一个新的shared_ptr对象指向该对象时,引用计数就会增加1,当一个shared_ptr对象被销毁时,引用计数就会减少1。当引用计数变为0时,就表示没有任何shared_ptr对象指向该对象,此时该对象将被自动销毁。 shared_ptr的优点是可以共享资源,缺点是有可能出现循环引用的问题,导致内存泄漏。为了避免这个问题,C++11引入了weak_ptr。 ## unique_ptr unique_ptr是一种独占式智能指针,它的实现原理是通过禁止拷贝和赋值来保证同一时间只有一个unique_ptr对象指向一个对象。当一个unique_ptr对象被销毁时,它所管理的对象也将会被销毁。为了更好地支持移动语义,C++11引入了move语义,使得unique_ptr对象可以被移动而不是被复制。 unique_ptr的优点是可移植性好,可以避免循环引用的问题,缺点是不能共享资源。 ## weak_ptr weak_ptr是一种弱引用智能指针,它的实现原理是与shared_ptr配合使用。weak_ptr不会增加引用计数,它只是提供了对所指向对象的一个非拥有性的访问。当所指向的对象被销毁后,weak_ptr将自动失效。 weak_ptr的优点是可以避免循环引用的问题,缺点是不能访问所指向对象的成员变量和成员函数。如果需要访问所指向对象的成员变量和成员函数,需要将weak_ptr转换为shared_ptr下面是一个示例代码,展示了shared_ptrunique_ptr、weak_ptr的使用方式: ```c++ #include <iostream> #include <memory> class A { public: A() { std::cout << "A()" << std::endl; } ~A() { std::cout << "~A()" << std::endl; } void foo() { std::cout << "foo()" << std::endl; } }; int main() { // shared_ptr std::shared_ptr<A> p1(new A()); std::shared_ptr<A> p2(p1); std::cout << "p1.use_count() = " << p1.use_count() << std::endl; std::cout << "p2.use_count() = " << p2.use_count() << std::endl; // unique_ptr std::unique_ptr<A> p3(new A()); // std::unique_ptr<A> p4(p3); // error: copy constructor is deleted std::unique_ptr<A> p5(std::move(p3)); if (p3 == nullptr) { std::cout << "p3 is nullptr" << std::endl; } // weak_ptr std::shared_ptr<A> p6(new A()); std::weak_ptr<A> p7(p6); std::cout << "p7.use_count() = " << p7.use_count() << std::endl; if (auto p8 = p7.lock()) { p8->foo(); } else { std::cout << "p7 is expired" << std::endl; } return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值