【C++】智能指针

  • Post author:
  • Post category:其他


C++中new就必须delete,但可能会因为忘记写或者代码跳转忘记delete,会导致内存泄漏,因此我们需要设计一种自主的内存回收机制,让

人为的开辟内存,系统释放内存,

就不需要delete。


智能指针

普通方法开辟内存:

用智能指针开辟内存:

开辟一个堆内存交给栈上一个有名称的对象来管理,在对象里定义一个指针指向堆内存


智能指针的设计思想:

当对象的生存周期到了,系统释放这个栈上的对象,针对于这个对象销毁系统先调用析构函数,可以把堆内存当成对象的其他资源一起销毁,这样就实现了人为开辟,系统释放的自主内存回收机制。


智能指针的分类:


1、auto_ptr(C++98)

2、unique_ptr(C++11)

3、shared_ptr(C++11)

4、weak_ptr(C++11)


1、auto_ptr

temlpate<typename T>
class Auto_Ptr
{
public:
	Auto_Ptr(T* ptr)
		:mptr(ptr)
	{}
	~Auto_Ptr()
	{
		delete mptr;
	}
	T* operator->()
	{
		return mptr;
	}
	T& operator*()
	{
		return *mptr;
	}
private:
	T* mptr;
};
class Test
{
public:
	void Show()
	{
		std::cout << "hello world!" << std::endl;
	}
};
int main()
{
	int* p = new int(10);
	Auto_Ptr<int> pa = new int(10);
	*p = 20;
	*pa = 20;

	Test* ptest = new Test();
	ptest->Show();

	Auto_Ptr<Test> apl = new Test();
	apl->Show();
	return 0;
	
}

(1)所有权唯一:旧智能指针所有权赋给新智能指针,取消掉旧智能指针的所有权

Auto_ptr<int> ap1 = new int;
Auto_ptr<int> ap2 = ap1;

针对于上述两行代码 ,它的设计是这样的:首先我们生成的是ap1这个对象,ap1这个对象中有一个指针mptr,这个mptr指向了一个堆内存。接下来通过拷贝构造生成了ap2这个对象。ap2中也有一个指针是mptr。同样ap2这个对象也指向了这个堆内存。如下图所示:

atuo_ptr会回收旧的智能指针的所有权,也就是ap2指向这个堆内存的时候,ap1就会变成旧的智能指针,同时会让ap1指向为空。保证了这个堆内存只有一个指针指向。也就是保证了这个智能指针所有权唯一的特点。如下图所示:

此时堆ap1失效了,进行解引用的时候程序会崩溃。

主要再拷贝构造函数中处理:

temlpate<typename T>
class Auto_Ptr
{
public:
 Auto_Ptr(T* ptr)
  :mptr(ptr)
 {}
 Auto_Ptr(Auto_Ptr<T>& rhs)
 {
 	mptr = rhs.Release();//先是旧的智能指针将原来的指向的堆内存保存下来,然后将旧的智能指针置为空,然后将原来保存下来的指针赋值给新的指针。就相当于新的智能指针指向了该堆内存,同时也取消掉了旧智能指针的所有权。
 }
 Auto_Ptr<T>& operator=(Auto_Ptr<T>& rhs)
 {
 	if(this != &rhs)
 	{
 		delete mptr;
 		mptr = rhs.Release();
 	}
 	return *this;
 }
 ~Auto_Ptr()
 {
  delete mptr;
 }
 T* operator->()
 {
  return mptr;
 }
 T& operator*()
 {
  return *mptr;
 }
private:
 T* Release()
 {
 	T* tmp = mptr;//首先将指针指向的堆内存保存下来
 	mptr = NULL;//然后将本身置成空
 	return tmp;//接下来将保存的地址返回出去
 }
 T* mptr;
};

(2)缺陷: 拷贝赋值过程中,可能导致智能指针提前失效


2、unique_ptr:

unique_ptr是借鉴scope_ptr实现的

(1)scope_ptr保证了所有权唯一,但禁止拷贝(不能用拷贝构造函数)和移动赋值(不能用赋值运算符重载函数)等操作转移权限,一个对象只能指向一个堆内存

(2)不能实现数据共享

template<typename T>
class Scope_Ptr
{
public:
	Scope_Ptr(T* ptr)
		:mptr(ptr)
	{}
	~Scope_Ptr()
	{
		delete mptr;
	}
	T* operator->()
	{
		return mptr;
	}
	T& operator*()
	{
		return *mptr;
	}
private:
	Scope_Ptr(const Unique_Ptr<T>&);//将拷贝构造函数和赋值运算符函数写在私有下
	Scope_Ptr<T>& operator=(const Unique_Ptr<T>&);
	T* mptr;
}
int main()
{
	Scope_Ptr<int> up1(new int);
	Scope_Ptr<int> up2(up1);//不允许多个智能指针指向同一个内存块
	return 0;
}


3、 shared_ptr(又称为强智能指针和带引用计数的指针)

(1)允许多个智能指针对象指向同一堆内存,实现了数据共享

(2)增加引用计数:纪录该堆内存有多少个对象指向;多一个对象指向,计数器+1;少一个对象指向,计数器-1,最后一个对象销毁,释放该堆内存。

(3)shared_ptr的问题:相互引用的问题,如果在内部相互指向,会产生内存泄漏


4、weak_ptr(弱智能指针)

为了解决强智能指针相互引用的问题,是为了解决强智能指针相互引用的问题,而且他不能单独使用.

weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得shared_ptr。

(1)不能单独使用,必须结合强智能指针的使用

(2)弱智能指针不处理引用计数



版权声明:本文为weixin_44915076原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。