【C++】智能指针

1.为什么需要智能指针

在异常的时候我们说过这里的问题,下面这种写法抛异常了会造成内存泄漏的问题。

int div()
{
   
   
	int a, b;
	cin >> a >> b;
	if (b == 0)
		throw invalid_argument("除0错误");

	return a / b;
}

void Func()
{
   
   
	// 1、如果p1这里new 抛异常会如何?
	// 2、如果p2这里new 抛异常会如何?
	// 3、如果div调用这里又会抛异常会如何?
	int* p1 = new int[10];
	int* p2 = nullptr;

	cout << div() << endl;


	delete[] p1;
	delete[] p2;
}

int main()
{
   
   
	try
	{
   
   
		Func();
	}
	catch (exception& e)
	{
   
   
		cout << e.what() << endl;
	}

	return 0;
}

前面我们的处理方式是把异常捕捉一下然后重新抛出。

void Func()
{
   
   
	// 1、如果p1这里new 抛异常会如何?
	// 2、如果p2这里new 抛异常会如何?
	// 3、如果div调用这里又会抛异常会如何?
	int* p1 = new int[10];
	int* p2 = new int[10];

	try {
   
   
		cout << div() << endl;
	}
	catch (...)
	{
   
   
		delete[] p1;
		delete[] p2;
		throw;
	}

	delete[] p1;
	delete[] p2;
}

但是这样的处理方式并不好。
第一代码很丑。
第二要注意到new的时候也会抛除异常。
p1这里抛异常倒是没事,如果抛异常直接跳转到main的catch进行捕捉下面都不会影响。但是p2抛了异常呢?p1已经成功申请空间了,如果不处理又会造成内存泄漏!

void Func()
{
   
   
	// 1、如果p1这里new 抛异常会如何?
	// 2、如果p2这里new 抛异常会如何?
	// 3、如果div调用这里又会抛异常会如何?
	int* p1 = new int[10];
	int* p2 = nullptr;

	try {
   
   
		p2 = new int[10];

		try {
   
   
			cout << div() << endl;
		}
		catch (...)
		{
   
   
			delete[] p1;
			delete[] p2;
			throw;
		}
	}
	catch(...)
	{
   
   
		delete[] p1;
		//...
 	}

	delete[] p1;
	delete[] p2;

}

如果又有p3,p4呢? 这里最本质的问题就是new本身就会抛异常。
这里真正的解决方式就是智能指针

我们看看智能指针内部是怎么实现的。

template<class T>
class Smartptr
{
   
   
public:
	Smartptr(T* ptr)
		:_ptr(ptr)
	{
   
   }

	~Smartptr()
	{
   
   
		//下面是[]这里暂时写成这个样子
		delete[] _ptr;
		cout << _ptr << endl;
	}
private:
	T* _ptr;
};


void Func()
{
   
   
	// 1、如果p1这里new 抛异常会如何?
	// 2、如果p2这里new 抛异常会如何?
	// 3、如果div调用这里又会抛异常会如何?
	int* p1 = new int[10];
	Smartptr<int> sp1(p1);
	int* p2 = new int[10];
	Smartptr<int> sp2(p2);


	cout << div() << endl;

	//不需要主动释放,无论是谁抛异常都能释放
	//delete[] p1;
	//delete[] p2;

}

在这里插入图片描述

抛异常栈帧会正常销毁,栈帧销毁这些对象就会出作用域,自定义类型出作用域就会调用析构函数,就会释放资源。

下面这里是把一个new出来的资源交给智能指针的对象,出了作用域就会自动把资源释放。

void Func()
{
   
   
	// 1、如果p1这里new 抛异常会如何?
	// 2、如果p2这里new 抛异常会如何?
	// 3、如果div调用这里又会抛异常会如何?
	int* p1 = new int[10];
	Smartptr<int> sp1(p1);
	int* p2 = new int[10];
	Smartptr<int> sp2(p2);

	cout << div() << endl;

	//不需要主动释放,无论是谁抛异常都能释放
	//delete[] p1;
	//delete[] p2;

}

有了智能指针抛异常带来的内存泄漏的问题就得到极大解决。

这里也可以直接把new出来的资源交给智能指针

void Func()
{
   
   
	// 1、如果p1这里new 抛异常会如何?
	// 2、如果p2这里new 抛异常会如何?
	// 3、如果div调用这里又会抛异常会如何?
	//int* p1 = new int[10];
	//Smartptr<int> sp1(p1);
	//int* p2 = new int[10];
	//Smartptr<int> sp2(p2);

	Smartptr<int> sp1(new int[10]);
	Smartptr<int> sp2(new int[10]);

	cout << div() << endl;
}

智能指针,也想解引用一下有什么方式可以处理一下呢?
可以重载一下。

template<class T>
class Smartptr
{
   
   
public:
	//RAII
	//构造---保存资源
	Smartptr(T* ptr)
		:_ptr(ptr)
	{
   
   }

	//析构---释放资源
	~Smartptr()
	{
   
   
		//下面是[]这里暂时写成这个样子
		delete[] _ptr;
		cout << _ptr << endl;
	}

	//像指针一样
	T& operator*()
	{
   
   
		return *_ptr;
	}

	T* operator->()
	{
   
   
		return _ptr;
	}

	T& operator[](size_t pos)
	{
   
   
		return _ptr[pos];
	}
private:
	T* _ptr;
};

下面就可以像普通指针一样去解引用等

void Func()
{
   
   
	//直接把new出来的资源交给智能指针
	Smartptr<int> sp1(new int[10]);
	Smartptr<int> sp2(new int[10]);

	*sp1 = 10;
	sp1[0]--;
	cout << *sp1 << endl;
	
	cout << div() << endl;
}

在这里插入图片描述

2.智能指针原理

智能指针的原理:

  1. RAII特性
  2. 重载operator*和opertaor->,具有像指针一样的行为。

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效(资源生命周期与对象声明周期进行绑定),最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:

  • 不需要显式地释放资源。
  • 采用这种方式,对象所需的资源在其生命期内始终保持有效

3.智能指针的使用以及问题

智能指针的使用我们已经见识到了。那它有没有什么问题呢?
就以我们刚才写的智能指针为例。

template<class T>
class Smartptr
{
   
   
public:
	//RAII
	//保存资源
	Smartptr(T* ptr)
		:_ptr(ptr)
	{
   
   }

	//释放资源
	~Smartptr()
	{
   
   
		delete _ptr;
		cout << _ptr << endl;
	}

	//像指针一样
	T& operator*()
	{
   
   
		return *_ptr;
	}

	T* operator->()
	{
   
   
		return _ptr;
	}

	T& operator[](size_t pos)
	{
   
   
		return _ptr[pos];
	}
private:
	T* _ptr;
};

int main()
{
   
   
	Smartptr<int> sp1(new int);
	Smartptr<int> sp2(sp1);

	return 0;
}

运行奔溃了,原因是什么?

评论 56
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值