C++_智能指针

1、智能指针的设计思想

我们一般通过在栈上创建一块内存管理堆上开辟的内存,根据堆栈的特点,我们可以知道,栈上的内存一般是由系统负责回收的!而堆上的内存则需要我们自己进行释放,忘记释放就会早成内存泄漏,因此,智能指针的设计思想就是为了满足内存由程序员开辟,然后由系统自动释放。就是将基本类型的指针封装为类对象模板,并在析构函数里释放的时候删除指向堆上的内存空间。

2、智能指针之auto_ptr

这是一个所有权唯一的智能指针,即管理权唯一,释放权唯一,当俩个指针指向同一块内存的时候,auto_ptr处理方案是将一个指向,一个置空,因此其使用场景比较单一。

代码实例如下:

template<typename T>
class Auto_ptr
{
public:
	Auto_ptr(T* ptr) :mptr(ptr){}  						//构造函数
	~Auto_ptr()   										//析构函数
	{
		delete mptr;
		mptr = NULL;
	}
	Auto_ptr(const Auto_ptr<T>& rhs)  					//拷贝构造
	{
		mptr = rhs.mptr;
		rhs.Release();                                  //变更管理权
	}
	Auto_ptr<T>& operator=(const Auto_ptr<T>& rhs)  	//赋值运算符重载
	{
		if (this != &rhs)  								//自赋值判断
		{
			delete mptr;  								//释放旧资源
			mptr = rhs.mptr;  							//开辟新资源
			rhs.Release(); 								//变更管理权
		}
		return *this;
	}
	T& operator*()   									// *运算符重载
	{
		return *mptr;
	}
	T* operator->()  									// ->运算符重载
	{
		return mptr;
	}
private:
	void Release()const   								//变更管理权,新指针有效,旧指针置空
	{
		(T*)mptr = NULL;
	}
	T* mptr;
};

3、智能指针之带标志位的auto_ptr

特点是所有权不唯一,释放权唯一,但是释放权可以转让。

template<typename T>
class Auto_ptr
{
public:
	Auto_ptr(T* ptr) :mptr(ptr), flag(true){}                  //构造函数
	Auto_ptr(const Auto_ptr<T>& rhs)			               //拷贝构造
	{
		mptr = rhs.mptr;
		flag = rhs.flag;
		rhs.flag = false;
	}
	Auto_ptr<T>& operator=(const Auto_ptr<T>& rhs)             //赋值运算符重载
	{
		if (this != &rhs)                                      //自赋值判断
		{
			this->~Auto_ptr();                                 //调用析构释放旧资源
			mptr = rhs.mptr;					               //赋值新资源
			flag = rhs.flag;					               //更新标志位
			rhs.flag = false;
		}
		return *this;
	}
	~Auto_ptr()                                                //析构函数
	{ 
		if (flag)
		{
			delete mptr;
		}
		mptr = NULL;
	}
	T& operator*()                                             // * 运算符重载
	{
		return *mptr;
	}
	T* operator->()                                           // ->指向运算符重载
	{
		return mptr;
	}
private:
	T* mptr;    
	mutable bool flag;                                       //是否拥有释放权
};   //mutable:去除常性,被mutable修饰的变量,将永远处于可变的状态,即使是const函数中也可以改变这个变量的值。

但是其也有缺点:释放权的转移有时候会导致堆内存提前释放。比如改智能指针不能作为函数参数,因为在函数堆栈调用过程中,实参传形参是一个初始化的过程,函数结束,栈帧回退,参数中有释放权的指针就会提前调用delete,释放内存。代码示例如下:

#include<iostream>

using namespace std;

void functional(Auto_ptr_flag<int> ptr)    		  //示例函数
{
	cout << "functional" << endl;
}

int main()
{
	Auto_ptr_flag<int> auto_flag1(new int);  	 //开辟一块堆内存
	Auto_ptr_flag<int> auto_flag2(auto_flag1);   //调用拷贝构造生成新的指针指针

	functional(auto_flag2);  					 //调用函数

	*auto_flag2 = 30;                   		 //函数已经退出,指针可能提前释放,存在bug
	cout << "acomplish" <<endl;
	
	return 0;
}

4、智能指针之score_ptr( 它能保证在离开作用域之后它所管理对象能被自动释放。)

scoped_ptr是boost库中智能指针,类似于c++11中unique_ptr,它是是boost库中的最简单的智能指针,它的特点是:一块堆内存,只允许一个引用,它能保证在离开作用域之后它所管理对象能被自动释放。scoped_ptr不能通过其他scoped_ptr共享控制权,因为其屏蔽了所有的拷贝构造函数和赋值运算符重载函数。为了更加严谨,除了将其私有化之外,还应该做到只申明,不实现的要求,因为友元可以访问类的私有成员。

代码实现如下:

#include<iostream> 
template<typename T>
class Scope_Ptr
{
public:
	Scope_Ptr(T* ptr) :mptr(ptr)       			     //构造函数
	{
		std::cout<<"Scope_Ptr(T* ptr) :mptr(ptr)"<<std::endl;	
	}
	~Scope_Ptr()                                     //析构函数
	{
		std::cout<<"~Scope_Ptr()"<<std::endl;
		delete mptr;
		mptr = NULL;
	}
	T* operator->()                                  // ->运算符重载函数
	{
		return mptr;
	}
	T& operator*()                                   // * 运算符重载函数
	{
		return *mptr;
	}
private:
	Scope_Ptr(const Scope_Ptr<T>&);                  //拷贝构造函数
	Scope_Ptr<T>& operator=(const Scope_Ptr<T>&);    //赋值运算符重载函数
	T* mptr;
};
int main()
{
	int* p = new int;
	Scope_Ptr<int> sp1(p);
	Scope_Ptr<int> sp2(p);
	Scope_Ptr<int> sp3(p);  //返回的都是一个地址
	return 0;
}

6、智能指针之share_ptr

share_ptr的特点是:带引用计数的智能指针,当有一个智能指针指向内存时,计数器加一;delete的时候,只有引用计数为0时,才可以释放。

class Ref_Management									//引用计数管理
{
public:

	static Ref_Management* getInstance()  				//提供公有接口,用于单例模式创建对象
	{
		return &rm;
	}
	
	void addref(void* mptr)								//添加引用计数
	{
		if (mptr != NULL)
		{
			int index = find(mptr);						//标记引用计数器的下表
			if (index < 0)
			{
				Node tmp(mptr, 1);						//没找到则进行创建引用计数器
				node[cursize++] = tmp;					//更新cuisize并添加引用计数器
            
				//Node node;
				//node[cursize].addr = mptr;
				//node[cursize].ref = 1;
				//cursize++;
		   
			}
			else
			{
				node[index].ref++;					    //引用计数加一
			}

		/
			//使用迭代器遍历
			//std::vector<Node>::iterator fit = find(mptr);
			//if (fit == vec.end())
			//{
			//	Node node(mptr, 1);
			//	vec.push_back(node);
			//}
			//else
			//{
			//	(*fit).ref++;
			//}
		/
		}
	}
	
	void delref(void* mptr)  		  				 		//删除引用计数
	{
		if (mptr != NULL) 			 				 		//参数检查
		{
			int index = find(mptr);   				 		//标记下标
			if (index < 0)
			{
				throw std::exception("addr is not exsit!");  //抛出异常
			}
			else
			{
				if (node[index].ref != 0)					 //判断引用计数是否为0
				{
					node[index].ref--;						 //引用计数减一
				}
			}
		}
	}

	int getref(void* mptr)  								 //获取引用计数
	{    
		int rt = -1;										 //标记引用计数器的下标
		if (mptr != NULL)
		{
			int index = find(mptr);							 //标记引用计数器的下表
			if (index >= 0)
			{
				rt = node[index].ref;//
			}
		}
		return rt;
	}
private:
	int find(void* mptr)  									//寻找引用计数
	{
		int rt = -1;										//标记引用计数器的下标
		for (int i = 0; i < cursize; i++)
		{
			if (node[i].addr == mptr)
			{
				rt = i;
				break;
			}
		}
		return rt;
	/
		//迭代器实现遍历
		//std::vector<Node>::iterator it = vec.begin();
		//for (it; it != vec.end(); it++)
		//{
		//	if ((*it).addr == mptr)
		//		break;
		//}
		//return it;
  	/
	}
private:
	class Node
	{
	public:
		Node(void* padd = NULL, int rf = 0) :addr(padd), ref(rf){}
	public:
		void* addr;  										//地址
		int ref;											//引用计数器
	};
	Node node[10];											//开辟十个引用计数器
	int cursize;											//下一个引用计数器的下表,也可以理解为引用计数
															//器的个数;类比vector的实现中的cursize
private:
	Ref_Management():cursize(0){} 							//构造函数私有化
	Ref_Management(const Ref_Management&);  				//拷贝构造函数私有化
	static Ref_Management rm;  								//用于生成唯一对象
};
Ref_Management Ref_Management::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)   								//自赋值判断
		{
			this->~Shared_Ptr();							//调用析构函数释放旧的资源
			mptr = rhs.mptr;								//赋予新的资源
			AddRef();										//添加引用计数
		}
		return *this;
	}
	~Shared_Ptr()   										//析构函数
	{
		DelRef();											//引用计数减一
		if (GetRef() == 0)									//判断引用计数是否为0
		{
			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);
	}
private:
	T* mptr;												//用一块栈上的内存,管理堆上的无名字的内存
	static Ref_Management* prm;   							//生成唯一对象
};
//静态成员变量类外进行初始化
template<typename T>
Ref_Management* Shared_Ptr<T>::prm = Ref_Management::getInstance();

缺点:share_ptr是强智能指针。交叉引用带来的问题:会造成内存泄漏,计数器会减少一次,但它本身被引用两次。这时我们发现,命名每个资源都只有一个对象来管理,却有两个引用计数,这也表示,在析构的时候会因为引用计数没有减到0而不释放其资源,造成内存泄漏。

智能指针之weak_ptr

weak_ptr智能指针的特点是:它是一个弱智能指针,不可单独使用,用于引用计数的管理,不能直接使用,需要和share_ptr配合使用。

代码示例如下:

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 Weak_ptr<T>& rhs)  //Weak_ptr自己的赋值运算符重载函数
	{
		if (this != &rhs)  							//自赋值判断
		{
			mptr = rhs.mptr; 						//直接赋予新资源
		}
		return *this;								
	}
	Weak_ptr<T>& operator=(const Shared_Ptr<T>& rhs)//传入Share_ptr智能指针的赋值运算符重载函数
	{
		mptr = rhs.getref();
		return *this;
	}
	~Weak_ptr()  									//析构函数
	{
	}
	T* operator->()									// ->运算符重载函数
	{
		return mptr;
	}
	T& operator*()									// *运算符重载函数
	{
		return *mptr;
	}
private:
	T* mptr;
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值