智能指针共分为4种,即boost库中的auto_ptr、scoped_ptr、shared_ptr、weak_ptr。
auto_ptr C++98标准, C++11后不再使用了
智能指针目的:由于堆内存的申请和释放都由程序员自己管理,所以会出现这样的情况:申请的空间在函数结束时忘记释放,造成内存泄漏。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间,避免内存泄露。
一、auto_ptr:
(管理权唯一,释放权唯一)
smart_ptr 问题:
1、 int *a = new int;
Smart_ptr<int> sp1(&a);
Smart_ptr<int> sp2(&a);
这样函数结束时会崩溃,释放两次a;
//(当管理权不唯一,释放权唯一时,可以解决这种问题:代标识位flage的智能指针)
//(1)旧的没有释放权,新的就没有:this->flag=rhs.flag;
//(2)旧的永远没有释放权
2、(管理权唯一,释放权唯一)情况下
Smart_ptr<int> sp1(new int);
Smart_ptr<int> sp2(sp1);
*sp1 = 10;//这句会崩溃。因为sp1保存的指针已经被置为NULL了,给NULL解引用,会崩溃。
二、scope_ptr 问题:
解决掉了上面的问题2,因为scope_ptr没法使用拷贝构造函数和赋值运算符的重载函数,没有给定义。
但还是没解决掉问题1。
template<typename T>
class Scope_ptr
{
public:
Scope_ptr(T* ptr = NULL) :mptr(ptr){}
~Scope_ptr()
{
delete mptr;
}
private:
Scope_ptr(const Scope_ptr<T>&);
Scope_ptr<T> operator =(const Scope_ptr<T>&);
T* mptr;
};
三、shared_ptr:
特点:专门设计一个引用计数管理器类,存储所引用的地址的个数。析构时,只要当前这个地址的引用计数不为0,就不释放资源,只将引用计数减1,当减为0时,才释放资源。
#include <iostream>
using namespace std;
class RefManagement
{
public:
static RefManagement* getInstance()//
{
return &rm;
}
private:
//屏蔽构造和拷贝构造函数
RefManagement() :cursize(0){}//将cursize初始化为0,意思当前有效的地址个数为0个
RefManagement(const RefManagement&);
static RefManagement rm;
public:
void addRef(void* ptr)//添加引用
{
if (ptr != NULL)
{
if(cursize == 10)//如果引用计数器已经放满了,就抛异常
{
throw exception("RefManagement is full");
}
int index = Find(ptr);//查找当前地址在Node数组里的下标,没有的话返回-1
if (index < 0)//没有
{
Node tmp(ptr, 1);//创建个Node对象
node[cursize++] = tmp;//将创建好的Node对象插入到node里数组里
}
else//找到了
{
node[index].count++;//引用计数加1
}
}
}
void delRef(void* ptr)//删除引用
{
if (ptr != NULL)
{
int index = Find(ptr);//查找下标
if (index < 0)//没找到,抛异常
{
throw exception("addr is not exsit");
}
else//找到了
{
int cnt = node[index].count;
if (cnt != 0)//只要计数没到0,就将计数减1
{
node[index].count--;
}
}
}
}
int getRef(void* ptr)//获取当前地址的引用计数
{
if (ptr == NULL)
{
return 0;
}
int index = Find(ptr);//查找当前地址的下标
if (index < 0)//没找到
{
return -1;//返回-1
}
return node[index].count;//找到了,相对应的引用计数
}
private:
int Find(void* ptr)//返回此地址在引用管理器中的下标//找到返回下标,没找到返回-1
{
for (int i = 0; i < cursize; ++i)
{
if (node[i].addr == ptr)
{
return i;
}
}
return -1;
}
class Node
{
public:
Node(void* ptr = NULL, int cnt = 0) :addr(ptr), count(cnt)
{}
public:
void* addr;//地址域
int count;//引用计数域
};
Node node[10];//此引用管理器能管理的地址的个数最多10个
int cursize;//当前有效的个数
};
RefManagement RefManagement::rm;
template<typename T>
class Shared_ptr
{
public:
Shared_ptr(T* ptr = NULL) :mptr(ptr)//构造//添加引用计数
{
AddRef();
}
Shared_ptr(const Shared_ptr<T>& rhs) :mptr(rhs.mptr)//拷贝构造//添加引用计数
{
AddRef();
}
Shared_ptr<T> operator =(const Shared_ptr<T>& rhs)//赋值运算符重载
{
if (this != &rhs)//判断自赋值
{
DelRef();//先减当前的引用计数
if (GetRef() == 0)//减完后,引用计数为0了,就delete
{
delete mptr;
}
mptr = rhs.mptr;//赋值
AddRef();//加引用计数
}
return *this;
}
T* GetPtr()const
{
return mptr;
}
~Shared_ptr()
{
DelRef();//减引用计数
if (GetRef() == 0)//减完后,值为0了,就delete
{
delete mptr;
}
mptr = NULL;
}
T* operator->()
{
return mptr;
}
T& operator*()
{
return *mptr;
}
private:
void AddRef()
{
prm->addRef(mptr);
}
void DelRef()
{
prm->delRef(mptr);
}
int GetRef()
{
return prm->getRef(mptr);
}
T* mptr;
static RefManagement* prm;//多个shared_ptr对象共用一个引用计数管理器
};
template<typename T>
RefManagement* Shared_ptr<T>::prm = RefManagement::getInstance();//初始化静态变量
上面是shared_ptr的实现,但shared_ptr这类强指针引用有个问题:循环引用,导致内存泄露。
循环引用:假设有个双向链表,有两个节点。第一个节点的指针为node1,第二个节点的指针为node2。
此时node1和node2的引用计数都为1.然后,node1里的next指针指向了node2,
node2里的ptr指针指向了node1,那么此时,node1和node2的引用计数就都为2了。
问题就来了:当这个双向链表的生存周期到了后,就要执行他们的析构函数,在执行析构函数时,
发现他们的引用计数值都为2,就都不会去真正析构,只会将其的引用计数减1。那么,引用计数减不为0,无法释放,这就会造成内存泄漏!
eg:
//智能指针相互引用
//智能指针相互引用
Shared_ptr<A> spa(new A());
Shared_ptr<B> spb(new B());
spa->pb = spb;//调用赋值运算符重载函数
spb->pa = spa;
四、 weak_ptr
为了解决循环引用,boost库引入了weak_ptr类。这也是个智能指针,他只是管理堆内存,并不释放内存,一般不单独使用,而是结合shared_ptr使用。它的析构函数也不会去delete。它就是那么"安静得看着你",指向而已。
这么看来,weak_ptr和普通指针几乎没区别,然而weak_ptr和普通指针一个比较大的区别是:
weak_ptr可以通过expired方法检测到所管理的对象是否已经被释放, 从而避免访问非法内存。
template<typename T>
class Weak_ptr
{
public:
Weak_ptr(T* ptr = NULL) :mptr(ptr){}
Weak_ptr(const Weak_ptr<T>& rhs) :mptr(rhs.mptr){}
Weak_ptr<T>& operator=(const Shared_ptr<T>& rhs)
{
mptr = rhs.GetPtr();
return *this;
}
~Weak_ptr()
{
mptr = NULL;
}
private:
T* mptr;
};