我们知道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,生成的代码会更小更快。
具体的new与make_shared的比较大家可以参考这个链接https://blog.csdn.net/coolmeme/article/details/43405155
我们来实现一个简单的shared_ptr(实现方法很多,我只列举一二)
//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表有一个特性,不能插入键相同的数据,虽然插入不会出错,但是不会改变原先的键所对应的值,后插的是插不进去的
未完。。。。