智能指针

    智能指针共分为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;
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值