简单实现shared_ptr

引言

这是一个简单的实现shared_ptr的过程 因为是小练习的缘故 其中有些地方逻辑可能并不严密 希望大家指正

注意点
  1. 删除器 因为shared_ptr的删除器是运行时绑定的 所以其类型应该是一个指针 所以我们需要一个函数指针 指向删除器
  2. 类的类型 这是一个典型的类指针的类 有共用一个指针 其实使用智能指针存储是最优的 但是我们就是在实现智能指针嘛 所以就操作普通指针就好
#include<iostream>
#include<string>
#include<set>
#include<map>
#include<vector>
#include<algorithm>
#include<memory>
#include<cstring>
using namespace std;

template <typename T> class Shared_ptr;

template<typename T,typename ... Args>
inline Shared_ptr<T> 
make_Shared(Args&& ... args){
    return Shared_ptr<int>(std::forward<Args>(args)...);
}
//这里只是简单的实现而已 其中并没有涉及到内存的分配 也就使得第一个参数有点浪费

template <typename T>
class Shared_ptr
{
    private:    
        size_t *Ptr_Count;//引用计数
        T* Mem_Ptr;//数据
        void (*Del_Ptr)(T *); //删除器 用于在运行时绑定删除器
    public:
        decltype(Del_Ptr) Return_Del(){return Del_Ptr;}
        //默认构造函数
        Shared_ptr():Ptr_Count(nullptr),Mem_Ptr(nullptr),Del_Ptr(nullptr){}

        //接收一个参数的构造函数(防止隐性转换)
        explicit Shared_ptr(T *tmp_ptr) try:
        Mem_Ptr(tmp_ptr),Ptr_Count(new size_t(1)),Del_Ptr(nullptr)
        {} catch(const std::bad_alloc &err) {cout << "error in new\n";}

        //不同类型转换
        template<class type>
        Shared_ptr(const Shared_ptr<type>& ptr) : Mem_Ptr(ptr->get()),Del_Ptr(ptr->Return_Del()),Ptr_Count(++(ptr->use_count())){}

        //拷贝构造函数
        Shared_ptr(Shared_ptr &tmp_ptr) noexcept(false){ //模板内部可简化为类名而不需要加类型
            Mem_Ptr = tmp_ptr.Mem_Ptr;
            Ptr_Count = tmp_ptr.Ptr_Count;
            Del_Ptr = nullptr;
            ++*Ptr_Count;
        }
        //拷贝赋值运算符
        void operator=(const Shared_ptr& tmp_ptr) & noexcept(false){
            Shared_ptr(std::move(tmp_ptr));//并没有什么数据成员 所以效率并没有什么提升 练下手
        }

        //不同类型转换
        template<class type>
        void operator=(const Shared_ptr<type>& tmp_ptr) & noexcept(false){
            Del_Ptr = tmp_ptr->Return_Del();
            Ptr_Count = ++(tmp_ptr->use_count());
            Mem_Ptr = tmp_ptr->get();
        }

        //移动构造函数
        Shared_ptr(Shared_ptr && tmp_ptr) noexcept:
        Mem_Ptr(tmp_ptr.Mem_Ptr),Ptr_Count(tmp_ptr.Ptr_Count),Del_Ptr(nullptr)
        {++*Ptr_Count;}

        //移动赋值运算符 //强制等号左边为左值
        void operator=(Shared_ptr && ptr) & noexcept{
            Shared_ptr(std::move(ptr));
        }

        //重载解引用运算符
        T& operator*(){
            return *Mem_Ptr;
        }
        T* operator->(){//至于为什么返回一个指针 因为Shared_ptr是一个类 类就应该返回一个成员
            return Mem_Ptr;
        } 
        T* get(){ //返回智能指针所保存的指针 操作危险
            return Mem_Ptr;
        }
        size_t use_count(){
            return *Ptr_Count;
        }
        bool unique(){
            return *Ptr_Count == 1;
        }
        void swap(Shared_ptr &tmp_ptr){
            //测试时使用原版本swap
            //std::swap(*this,tmp_ptr); 这里概念还有问题 搞懂move原理以后再来

            //交换指针效率更高
            std::swap(Mem_Ptr,tmp_ptr.Mem_Ptr);
            std::swap(Ptr_Count,tmp_ptr.Ptr_Count);
            std::swap(Del_Ptr,tmp_ptr.Del_Ptr);
        }
        void reset(){ //当reset为空时 只是把reset的指针
            if(*Ptr_Count == 1){
                delete Mem_Ptr;
                *Ptr_Count = 0; //防止析构时两次delete 用Ptr_count在析构时进行特判
            }else {
                Mem_Ptr = nullptr;
                --*Ptr_Count;
            }
        }
        void reset(T *tmp_ptr){
            if(*Ptr_Count==1){ //本对象是唯一对象则会释放   //不唯一的话其他对象还需要这两个指针
                Del_Ptr ? Del_Ptr(Mem_Ptr) : delete Mem_Ptr;
                delete Ptr_Count;
            }else{
                --*Ptr_Count; 
            }
            Mem_Ptr = tmp_ptr;
            Ptr_Count = new size_t(1);//看起来很奇怪 仔细想想 如果count不为零的话其他智能指针还要使用
        }
        //接收一个指针和一个自定义的删除器 用于在析构时正确释放对象 默认为delete
        void reset(T *tmp_ptr,void (*del_ptr)(T *)){
            if(*Ptr_Count==1){
                delete Mem_Ptr;
                delete Ptr_Count;
            }
            --(*Ptr_Count);
            Mem_Ptr = tmp_ptr;
            Ptr_Count = new size_t(1);
            Del_Ptr = del_ptr; //定义一个删除器
        }
        ~Shared_ptr(){
            try{
                cout << "Commen complete the destructor.\n";

                if(Mem_Ptr == nullptr && Ptr_Count == nullptr){ //防止默认构造出现对仅有delete 而无new
                    return;
                }

                if((*Ptr_Count) == 0){ //证明被reset了 特殊处理
                    delete Ptr_Count;
                    Ptr_Count = nullptr;
                    return;
                }
                --*Ptr_Count;
                if(*Ptr_Count==0){
                    Del_Ptr ? Del_Ptr(Mem_Ptr) : delete Mem_Ptr;
                    delete Ptr_Count;
                    Ptr_Count = nullptr;
                    Mem_Ptr = nullptr;
                }
            }catch(...){
                cout << "error in delete\n";
            }
        }
};

//一个测试用的聚合类
struct text{
    int ans;
    int weight;
};

void Del(text* tmp){//聚合类 text 的删除器
    delete tmp;
    cout << "Special complete the destructor.\n";
}

//demo
int main()
{
    cout << "测试make_shared:\n";
    Shared_ptr<int> str=make_Shared<int>(new int(5));
    cout << ": " << *str << endl;
    cout << "测试基本操作\n";
    Shared_ptr<int> tmpa_ptr(new int(10));
    cout << *tmpa_ptr << endl;
    cout << tmpa_ptr.use_count() << endl;
    Shared_ptr<int> tmpb_ptr(tmpa_ptr);
    cout << *tmpb_ptr << endl;
    cout << tmpb_ptr.use_count() << endl;

    cout << "交换操作\n";
    Shared_ptr<int> tmp_swap(new int(5));
    cout << *tmpa_ptr << " " << *tmp_swap << endl;
    tmpa_ptr.swap(tmp_swap);
    cout << *tmpa_ptr << " " << *tmp_swap << endl;

    cout << "测试三种参数的reset函数\n";
    tmpa_ptr.reset();
    cout << tmpa_ptr.use_count() << " " << tmpb_ptr.use_count() << endl;

    shared_ptr<text> tmpc_ptr(new text({5,5}));
    cout << (*tmpc_ptr).ans << " " << (*tmpc_ptr).weight << endl;
    tmpc_ptr.reset(new text({10,10}));
    cout << (*tmpc_ptr).ans << " " << (*tmpc_ptr).weight << endl;
    tmpc_ptr.reset(new text({20,20}),Del);//shared_ptr删除器运行时绑定 所以使用指针存储删除器

    cout << "测试get函数\n";
    text *temp_text_ptr = tmpc_ptr.get(); //此函数小心使用 会返回智能指针所维护的指针域
    cout << temp_text_ptr->ans << " " << temp_text_ptr->weight << endl;

    cout << "开始析构\n";

    return 0;
}

这是测试代码的输出

测试make_shared:
: 5
测试基本操作
10
1
10
2
交换操作
10 5
5 10
测试三种参数的reset函数
0 2
5 5
10 10
测试get函数
20 20
开始析构
Special complete the destructor.
Commen complete the destructor.
Commen complete the destructor.
Commen complete the destructor.
Commen complete the destructor.

在本篇博客中只是简单实现了make_shared 就像我们注释中所说的 这样的写法导致make_shard的第一个参数有些多余 但实际一定不是这样的 我们来看看make_shared的源码

  template<typename _Tp, typename... _Args>
    inline shared_ptr<_Tp>
    make_shared(_Args&&... __args)
    {
      typedef typename std::remove_const<_Tp>::type _Tp_nc;
      return std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
                       std::forward<_Args>(__args)...);
    }

我们可以看到第一个类型参数的存在是很有必要的 为了能够正确的分配内存  我们这个模板函数也应该由我们显式的提供类型

原文链接: https://www.cnblogs.com/lizhaolong/p/16437389.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    简单实现shared_ptr

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/395817

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月5日 下午1:43
下一篇 2023年4月5日 下午1:43

相关推荐