避免异常发生时的堆内存泄漏——智能指针auto_ptr

自己编的两个小例子,感觉贼好懂!

#include<iostream>
using namespace std;
enum index { underflow, overflow };//index是枚举类型
int array_index(int *a, int n, int index);
void fun() {
	int n = 10;
	int *a = new int[n];
	for (int i = 0; i < 10; i++)
		a[i] = i;
	try
	{
		cout << array_index(a, n, 5) << endl;//此处不会捕获异常,所以可以执行下一句
		cout << array_index(a, n, -1) << endl;
	}
	catch (...) {
		delete a;
		throw;
	}
	delete a;
}
int main()
{
	try {
		fun();
	}
	catch (index e)
	{
		if (e == underflow)
		{
			cout << "index underflow!" << endl;
		}
		if (e == overflow)
		{
			cout << "index overflow!" << endl;
		}
	}


	system("pause");
	return 0;
}
int array_index(int *a, int n, int index)
{
	if (index < 0) throw underflow;
	if (index > n - 1) throw overflow;
	return a[index];
}

在这个例子中,在fun函数中如果遇到了数组越界,会向主调函数抛出异常,那么要特别注意,在执行throw;之前,我们必须先释放堆内存,否则会内存泄漏。而且如果fun函数没有遇到异常,那么别忘了在fun函数结尾也要释放内存。

但是有时经常会忘记释放内存,所以一个捷径是把一切资源包装成对象,把对象定义为栈上的局部变量,这时当有异常抛出时,fun函数的调用就会结束,此时就会自动调用析构函数。

我修改程序如下,设计了一个简单的动态数组类:

#include<iostream>
using namespace std;
enum index { underflow, overflow };//index是枚举类型
class Array {
public:
	Array(int n) :n(n) {
		a = new int[n];
		for (int i = 0; i < n; i++)
			a[i] = 0;
	}

	~Array() { delete a; cout << "destructor" << endl; }

	int array_index(int index)
	{
		if (index < 0) throw underflow;
		if (index > n - 1) throw overflow;
		return a[index];
	}
	int *a;
private:
	int n;
};
void fun() {
	Array a(10);//在fun函数中创建一个动态数组类的对象
	for (int i = 0; i < 10; i++)
		a.a[i] = i;
	try
	{
		cout << a.array_index(5) << endl;//此处不会捕获异常,所以可以执行下一句
		cout << a.array_index(-1) << endl;
	}
	catch (...)
	{
	   throw;	
	}
}
int main()
{
	try {
		fun();
	}
	catch (index e)
	{
		if (e == underflow)
		{
			cout << "index underflow!" << endl;
			
		}
		if (e == overflow)
		{
			cout << "index overflow!" << endl;
		}
	}
	cout << "That is ok." << endl;
	system("pause");
	return 0;
}

在这里插入图片描述

智能指针auto_ptr

引入主题,为了更加省力地维护资源,避免内存泄漏,C++标准库中提供了一种叫智能指针的工具(面试必考)!!!
auto_ptr是一个类模板,使用时需要头文件memory,auto_ptr模板有一个类型参数X,例如,auto_ptr<int>就是一个指向int型数据的智能指针。
简单来说,每一个auto_ptr的对象都关联着一个指针。在构造一个智能指针的对象时,可以指定与它关联的指针,它的构造函数原型如下(可见,是个显式构造函数):
explicit auto_ptr(X*p=0)throw();
该构造函数的参数p就表示该智能指针初始化后所关联的普通指针,如果省略该参数,则p取默认值空指针,这时智能指针对象初始化后关联的指针为空。
一个智能指针对象一旦关联到一个普通指针,就意味着删除该指针所指向的堆对象的责任将由该智能指针对象承担(除非调用了智能指针的release函数,或该智能指针又被赋给了其他智能指针,后文详细说明)。一个智能指针对象在析构时,会自动对它所关联的指针执行delete,这样就无需手动释放内存了,这就是最大优点。
注意:auto_ptr只能关联到一个指向堆对象的指针(即用new生成对象的指针),不能关联到其他对象的指针,也不能关联到用new动态分配的数组,否则会在智能指针对象析构时出问题。(很重要,不能关联到用new动态分配的数组!!!因为auto_ptr在析构的时候只是调用delete,而数组应该要调用delete[])

对于一个构造好的智能指针对象,可以使用成员函数get得到它关联的指针:

X*get()const throw();

不过,多数情况下,无须调用get函数,因为auto_ptr的*->都被重载了,所以可以像普通指针那样去操作一个智能指针对象,对于一个智能指针对象ap,*ap等价于*(ap.get()),而ap->fun()就等价于ap.get()->fun(),这给智能指针的使用提供了很大的便利。

使用reset函数可以改变一个智能指针对象所关联的普通指针:

void reset(X*p=0)throw();

调用该函数会使当前智能指针对象关联到新指定的指针p,需要注意的是:如果在执行该函数前该智能指针关联的旧指针不为p,则会对原来关联的旧指针执行delete操作。

使用release函数可以解除一个智能指针对象与当前指针的关联:

X*release()throw();

该函数会使当前的智能指针对象所关联的指针变为空,然后返回智能指针对象原来关联的指针,但不会对该普通指针执行delete操作。

智能指针对象之间可以赋值,也可以用一个智能指针对象复制构造另一个智能指针对象。但与普通的赋值和复制构造不同的是,因为至多只能有一个智能指针对象负责同一个指针的删除,所以,一旦用一个智能指针对象为另一个智能指针对象赋值或者用一个智能指针对象复制构造另一个智能指针对象,该智能指针对象会解除与当前普通指针的关联。例如:

auto_ptr<X>ap2(ap1);//复构函数

等价于

auto_ptr<X>ap2(ap1.release());//构造函数

而执行赋值ap2=ap1;等价于ap2.reset(ap1.release());

将auto_ptr对象定义在栈上,可以很方便地删除堆对象,例如:

#include<iostream>
#include<memory>
using namespace std;
class test {
};
int main()
{
	test*p = new test();
	auto_ptr<test>ap(p);
	//auto_ptr<test>ap = new test();这样写是错误的,因为auto_ptr的构造函数是显式的
	system("pause");
	return 0;
}

这样,当函数结束时,会自动调用智能指针对象的析构函数,从而自动对它所关联的指针执行delete。出现异常时,同理。
注意:auto_ptr<test>ap = new test();这样写是错误的,因为auto_ptr的构造函数是显式的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值