现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

(二)智能指针—-shared_ptr/weak_ptr

2017-06-28 11:46 工业·编程 ⁄ 共 2610字 ⁄ 字号 暂无评论

我们知道auto_ptr,unique_ptr构造或者析构后,都会将自身的管理权限转移,那么如何解决这个问题呢,boost库也给我们提供了带引用计数的指针

1:shared_ptr

shared_ptr是最常用的智能指针,shared_ptr采用了引用计数器,多个shared_ptr中的T *ptr指向同一个内存区域(同一个对象),并共同维护同一个引用计数器。shared_ptr定义如下,记录同一个实例被引用的次数,当引用次数大于0时可用,等于0时释放内存。所以用户可以自由的赋值和构造,只要其引用计数不为0,对象所指的内存就不会被析构掉,也就不会产生错误。

在shared_ptr的操作中,我们对于动态内存的开辟及初始化最好是使用make_shared这个函数模板,而不是使用new,为什么呢?

同直接使用new相比,make函数减小了代码重复,提高了异常安全,并且对于std::make_shared和std::allcoated_shared,生成的代码会更小更快。另外使用 std::shared_ptr 如果用 new 来调用,这使得代码出现了某种程度上的不对称。于是在编码中尽量不使用new,使用make_shared,函数模板。std::make_shared 就能够用来消除显示的使用 new,所以std::make_shared 会分配创建传入参数中的对象,并返回这个对象类型的std::shared_ptr指针。

具体的new与make_shared的比较大家可以参考这个链接https://blog.csdn.net/coolmeme/article/details/43405155

可以看出,我们并不能隐式的用new来构造对象,但是我们可以用make_shared这个函数模板隐式构造。

shared_ptr可以通过get()函数获取到原始指针(获取的时候并不会增加引用计数),还可以通过reset()函数减少一个引用计数,相当于将一个智能指针对象指向NULL;看以通过use_count()函数获取引用计数。

#include<iostream>

using namespace std;

int main()

{

    shared_ptr<int> s1 = make_shared<int>(20);

    shared_ptr<int> s2 = s1;  // 引用计数+1

    shared_ptr<int> s3 = s1;  // 引用计数+1

    int *p = s1.get();   //只能用它的原始类型来接收,当然void *也可以,这样不会增加引用计数

  

    cout<<"s1.use_count() = "<<s1.use_count()<<endl;     // 3

    cout<<"s2.use_count() = "<<s2.use_count()<<endl;    // 3

    cout<<"s3.use_count() = "<<s3.use_count()<<endl;    // 3

  

    s2.reset();   //将

    cout<<"reset s2:"<<endl;

    cout<<"s1.use_count() = "<<s1.use_count()<<endl;     // 2

    cout<<"s2.use_count() = "<<s2.use_count()<<endl;    // 0,s2已经reset

    cout<<"s3.use_count() = "<<s3.use_count()<<endl;    // 2

}

我们来实现一个简单的shared_ptr(实现方法很多,我只列举一二)

<一>指针与引用计数绑定,用map表实现

//shared_ptr 引用计数用map表来表示的第一种实现方式

template<typename T>

class SharedPtr

{

public:

    SharedPtr(T *p=NULL):ptr(p)

    {

    mmap.insert(make_pair(ptr,1));  //将ptr及其引用计数插入map表中

    }

    SharedPtr(const SharedPtr & rt)  //拷贝构造函数

    {

    ptr=rt.ptr;   

    mmap[ptr]++;   //通过mmap[]里的键就可以访问到键所对应的值,将值增加

    }

    SharedPtr& operator=(const SharedPtr & rt)

    {

        if(&rt!=this)     //判断自赋值

        {

        if(--mmap[ptr]<=0)  //如果被赋值的对象只有一个指向那块内存,就要讲内存释放,并且在map表中将数据擦去

        {

            if(ptr!=NULL)

             {

                 delete ptr;   //释放内存

             ptr=NULL;

         }

       mmap.erase(ptr);   //擦去map表中的数据

        }

        ptr=rt.ptr;

        mmap[ptr]++;

        return *this;

    }

    }

    T &operator *()

    {

    return *ptr;

    }

    T *operator->()

    {

    return ptr;

    }

    ~SharedPtr()

    {

           if(--mmap[ptr]<=0)  //判断是否此对象是内存指向的最后一个对象

    {

        if(ptr!=NULL)

        {

        delete ptr;  //释放掉内存

        ptr=NULL;

        }

        mmap.erase(ptr);   //在map表中擦掉数据

    }

    }

private:

        static map<T *,int>mmap;   //同一个类共用一个map表

    T *ptr;

    

};

template<typename T>

map<T *,int> SharedPtr<T>::mmap; //map表有一个特性,不能插入键相同的数据,虽然插入不会出错,但是不会改变原先的键所对应的值,后插的是插不进去的

  

未完。。。。

给我留言

留言无头像?