智能指针

本文介绍了C++中的四种智能指针:auto_ptr(已弃用),unique_ptr,shared_ptr和weak_ptr。智能指针用于管理内存,防止内存泄漏。auto_ptr在C++11中被弃用,unique_ptr具有所有权唯一,禁止拷贝的特点。shared_ptr支持共享拥有,使用引用计数,可能导致循环引用问题。weak_ptr用于解决shared_ptr的循环引用问题,不参与对象的内存管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,但不指向引用计数的共享内存,但是其可以检测到所管理的对象是否已经被释放,从而避免非法访问。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值