C++里面的四个智能指针:auto_ptr,shared_ptr,weak_ptr,unique_ptr其中后三个式C++11支持,并且第一个已经被11弃用。
使用智能指针的原因
智能指针的作用是管理一个指针,因为存在一下情况:申请的空间在函数结束结束的时候忘记了释放,造成内存泄漏,使用智能指针可以很大程度上避免这个问题,因为智能指针就是一个类,当超出类的作用域时,类就会调用析构函数,析构函数自动释放资 源,所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。
1. auto_ptr(c++98的方案,cpp11已经抛弃)
特点:
①. 采用所有权模式,②新的智能指针取消旧智能指针的所有权 ③赋值或者拷贝导致智能指针提前失效。
template <class T>
class Auto_ptr{
public:
Auto_ptr(T*ptr = NULL):mptr(ptr){}
~Auto_ptr()
{
delete mptr;
mptr = NULL;
}
Auto_ptr(Auto_ptr<T>& res)
{
mptr = res.Release();
}
Auto_ptr<T>&operator=(Auto_ptr<T>&res)
{
if(this != &res)//先判断是否为自赋值
{
delete mptr;
mptr = res.Release();
}
return *this;
}
private:
T*Release()
{
T*ptr = mptr;
mptr = NULL;
return ptr;
}
T*mptr;
};
int main()
{
Auto_ptr<int>ap1 (new int);
Auto_ptr<int>ap2;
ap2 = ap1;//注意:不会报错
return 0;
}
2.unique_ptr(替换auto_ptr)
unique_ptr实现独占式拥有或严格拥有概念,保证一时间内只有一个智能指针可以指向该对象。它对于避免资源泄漏特别特别有用。
特点:①所有权唯一 ②禁止拷贝构造和赋值运算符重载 ③不能数据共享
template <class T>
class Unique_ptr{
public:
Unique_ptr(T*ptr ):mptr(ptr){}
~Unique_ptr()
{
cout<<"~Unique_ptr"<<endl;
delete mptr;
mptr = NULL;
}
private:
Unique_ptr(Unique_ptr<T>& res);
Unique_ptr<T>&operator=(Unique_ptr<T>&res);
T*Release()
{
T*ptr = mptr;
mptr = NULL;
return ptr;
}
T*mptr;
};
template <class T>
Unique_ptr<T>::Unique_ptr(Unique_ptr<T>& res)
{
mptr = res.Release();
}
template <class T>
Unique_ptr<T>&Unique_ptr<T>::operator=(Unique_ptr<T>&res)
{
if(this != &res)//先判断是否为自赋值
{
delete mptr;
mptr = res.Release();
}
return *this;
}
int main()
{
Unique_ptr<int>ap1 (new int(4));
Unique_ptr<int>ap2(new int(5));
int *p = new int(20);
Unique_ptr<int> up1(p);
Unique_ptr<int> up2(p);
Unique_ptr<int> up3(p);
//由上面四行代码可以看出,我们虽然把拷贝和赋值两个函数放在私有里面,
//但是我们可以手动让多个指针指向同一块内存,在调用析构函数后会崩溃的。
//ap2 = ap1;//错误,不能调用赋值用算符重载函数
return 0;
}
运行结果为:
重复释放内存,导致运行程序崩溃。
3.shared_ptr(强智能指针)
shared_ptr实现了共享式拥有概念。多个智能指针可以指向多个相同对象,该对象和其相关的资源会在"最后一个引用被销毁"的时候被销毁。从名字shared就可以看出资源可以被多个智能指针共享,它使用计数机制来表明资源被几个指针共享,可以通过成员函数use_count()来查看资源的所有者个数,除了可以通过new来构造,还可以通过传入auto_ptr.
#include<iostream>
using namespace std;
/*-----引用计数的类-----*/
class Ref_Manage{
public:
static Ref_Manage* getInstance()
{
if(prm == NULL)
{
prm = new Ref_Manage();
}
return prm;
}
void addRef(void* ptr)
{
if(ptr == NULL)
return ;
int index = find(ptr);
if(index != -1)
{
ref_count[index].count++;//该地址已经存在
}
else//该地址不存在
{
ref_count[cursize].addr= ptr;
ref_count[cursize++].count = 1;
}
}
void deleRef(void* ptr)
{
if(ptr == NULL) return;
int index = find(ptr);
if(index != -1)//找到要删的下标
{
ref_count[index].count--;
}
}
int getRef(void *ptr)//得到引用计数的个数
{
if(ptr == NULL) return 0;
int index = find(ptr);
if(index == -1)
{
throw std::exception("ptr is error!");
}
return ref_count[index].count;
}
private:
Ref_Manage()
{
cursize = 0;
}
Ref_Manage(const Ref_Manage& rhs);
int find(void*ptr)
{
int index = -1;
for(int i =0;i<cursize;i++)
{
if(ref_count[i].addr == ptr)
{
index = i;
break;
}
}
return index;
}
typedef struct ref
{
void* addr;
int count;
}Ref;
Ref ref_count[10];
int cursize;
static Ref_Manage* prm;
};
Ref_Manage* Ref_Manage:: prm = NULL;
/*——Shared_ptr类的实现——*/
template <class T>
class Shared_ptr{
public:
Shared_ptr(T* ptr = NULL)
{
mptr = ptr;
rm->addRef(ptr);
}
Shared_ptr(Shared_ptr<T>& rhs)//浅拷贝
{
mptr = rhs.mptr;
rm->addRef(mptr);
}
Shared_ptr<T> operator=(Shared_ptr<T> &rhs)
{
if(this != &rhs)
{
rm->deleRef(mptr);//释放旧资源
if(rm->getRef(mptr) == 0)
{
delete mptr;
}
mptr = rhs.mptr;
rm->addRef(mptr);
}
return *this;
}
~Shared_ptr()
{
rm->deleRef(mptr);
if(rm->getRef(mptr) == 0)
{
delete mptr;
}
mptr = NULL;
}
private:
T* mptr;
static Ref_Manage * rm;
};
template<class T>
Ref_Manage *Shared_ptr<T> :: rm =Ref_Manage::getInstance();//单利模式
int main()
{
Shared_ptr<int>ap1(new int());
Shared_ptr<int>ap2 = ap1;
Shared_ptr<int>ap3;
ap3 = ap2;
return 0;
}
运行结果为:
缺点:
对于shared_ptr(),也存在一部分的缺点,shared_ptr相互引用时会发生锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。
#include<iostream>
using namespace std;
/*-----引用计数的类-----*/
class Ref_Manage{
public:
static Ref_Manage* getInstance()
{
if(prm == NULL)
{
prm = new Ref_Manage();
}
return prm;
}
void addRef(void* ptr)
{
if(ptr == NULL)
return ;
int index = find(ptr);
if(index != -1)
{
ref_count[index].count++;//该地址已经存在
}
else//该地址不存在
{
ref_count[cursize].addr= ptr;
ref_count[cursize++].count = 1;
}
}
void deleRef(void* ptr)
{
if(ptr == NULL) return;
int index = find(ptr);
if(index != -1)//找到要删的下标
{
ref_count[index].count--;
}
}
int getRef(void *ptr)//得到引用计数的个数
{
if(ptr == NULL) return 0;
int index = find(ptr);
if(index == -1)
{
throw std::exception("ptr is error!");
}
return ref_count[index].count;
}
private:
Ref_Manage()
{
cursize = 0;
}
Ref_Manage(const Ref_Manage& rhs);
int find(void*ptr)
{
int index = -1;
for(int i =0;i<cursize;i++)
{
if(ref_count[i].addr == ptr)
{
index = i;
break;
}
}
return index;
}
typedef struct ref
{
void* addr;
int count;
}Ref;
Ref ref_count[10];
int cursize;
static Ref_Manage* prm;
};
Ref_Manage* Ref_Manage:: prm = NULL;
/*——Shared_ptr类的实现——*/
template <class T>
class Shared_ptr{
public:
Shared_ptr(T* ptr = NULL)
{
mptr = ptr;
rm->addRef(ptr);
}
Shared_ptr(Shared_ptr<T>& rhs)//浅拷贝
{
mptr = rhs.mptr;
rm->addRef(mptr);
}
Shared_ptr<T> operator=(Shared_ptr<T> &rhs)
{
if(this != &rhs)
{
rm->deleRef(mptr);//释放旧资源
if(rm->getRef(mptr) == 0)
{
delete mptr;
}
mptr = rhs.mptr;
rm->addRef(mptr);
}
return *this;
}
T*operator->()
{
return mptr;
}
~Shared_ptr()
{
rm->deleRef(mptr);
if(rm->getRef(mptr) == 0)
{
delete mptr;
}
mptr = NULL;
}
private:
T* mptr;
static Ref_Manage * rm;
};
template<class T>
Ref_Manage *Shared_ptr<T> :: rm =Ref_Manage::getInstance();//单利模式
class B;
class A
{
public:
A()
{
cout<<"A()"<<endl;
}
~A()
{
cout<<"B()"<<endl;
}
public:
Shared_ptr<B> spa;
};
class B
{
public:
B()
{
cout<<"B::B()"<<endl;
}
~B()
{
cout<<"B::~B"<<endl;
}
public:
Shared_ptr<A>spb;
};
int main()
{
Shared_ptr<int>ap1(new int());
Shared_ptr<int>ap2 = ap1;
Shared_ptr<int>ap3;
ap3 = ap2;
Shared_ptr<A>pa(new A());
Shared_ptr<B>pb(new B());
pa->spa = pb;
pb->spb = pa;
return 0;
}
运行的结果为:可以看到(有内存泄漏的情况,因为先释放了pah和pb就找不到了我们所指向那个的内存)
4. weak_ptr
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr. weak_ptr只是提供了对管理对象的一个访问手段。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。
为了解决循环引用导致的内存泄漏,引入了weak_ptr弱指针,weak_ptr的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,但不指向引用计数的共享内存,但是其可以检测到所管理的对象是否已经被释放,从而避免非法访问。