C++ 中异常处理 层次结构 内存

异常的使用准则

先以window的处理方式做以下介绍:

调用throw()时不要抛出异常对象(这里假设为e)的指针,因为等到catch代码块执行时e所对应的内存已被销毁,正确使用方式时,throw一个e对象本身,而不是其指针,实际上,throw()前,会在当前函数 之外的 接下来的 栈空间 上调用e的copy构造函数,重新实例化另一个e,因为throw()函数调用后,catch代码块调用之前,会将catch代码块所在的函数(下例中为main函数,这里我们也假设为main函数)栈空间,到新的e之间的所有函数的栈空间全部销毁,执行catch代码块时使用e之后的栈空间,例如调用一个函数,肯定会使用e之后的栈空间,但是catch代码块中的定义变量时可能会用到main函数和对象e之间空闲的栈空间(可能编译时将变量的声明做了调整),如下window执行结果:

main函数中addr_tool的地址为:004FFBC0
请输入期望发生那种异常,1或2

1
BadProcType构造函数do
Divide x地址为:004FFAA4
Divide y地址为:004FFAA8
divide x地址为:004FF97C
divide y地址为:004FF980
BadProcType构造函数do
divide e地址为:004FF944
before throw e in divide
BadProcType copy构造函数do
BadProcType析构函数: the object "e" has been constructed in function divide released
BadProcType析构函数: the object "e" has been constructed in function Divide released

 catch begin
addr_test函数中的addr_tool的地址为:004FEF18
catch 块catch到的e地址为:004FF830
catch 块中addr_tools的地址为:004FFB9C
catch end

BadProcType析构函数: the object "e" has been constructed in throw(e) released
main 中b的地址为:004FFB84
请按任意键继续. . .

以上文字表述的内存模型如下


linux的处理方式是,在调用throw()函数时,会在另一块内存(非栈空间)上分配一块内存,供为新e对象,根据输出结果,有点不可思议,地址很低,只有7位(16进制),根据地址值计算,已经到代码段(.text)之前。然后销毁main函数之后所有函数的栈空间,然后执行catch代码块,执行catch时,使用main函数之后的栈空间,执行结果如下:

main函数中addr_tool的地址为:0x7ffff4383f4c
请输入期望发生那种异常,1或2
1
BadProcType构造函数do
Divide x地址为:0x7ffff4383efc
Divide y地址为:0x7ffff4383ef8
divide x地址为:0x7ffff4383e8c
divide y地址为:0x7ffff4383e88
BadProcType构造函数do
divide e地址为:0x7ffff4383ea0
before throw e in divide
BadProcType copy构造函数do
BadProcType析构函数: the object "e" has been constructed in function divide released
BadProcType析构函数: the object "e" has been constructed in function Divide released

 catch begin
addr_test函数中的addr_tool的地址为:0x7ffff4383f2c
catch 块catch到的e地址为:0x1a36110
catch 块中addr_tools的地址为:0x7ffff4383f40
catch end

BadProcType析构函数: the object "e" has been constructed in throw(e) released
main 中b的地址为:0x7ffff4383f48

例程代码如下:

#include <iostream>
#include <string>


using namespace std;


class BadProcType
{
public:
	BadProcType(string str = "")
	{
		flag = str;
		cout << "BadProcType构造函数do" << endl;
		//cout << "flag地址为:" << &flag << endl;
	}
	BadProcType(const BadProcType &)
	{
		cout << "BadProcType copy构造函数do" << endl;
		//cout << "flag地址为:" << &flag << endl;
	}
	~BadProcType()
	{
		cout << "BadProcType析构函数: the object \"e\" has been constructed " << flag << " released" << endl;
	}
	string flag;
};


//发生异常之后,是跨函数的
void divide(int x, int y)
{
//异常1
	if (y == 0)
	{
		printf("divide x地址为:%p\n", &x);
		printf("divide y地址为:%p\n", &y);
		//int e  = x;
		//printf("divide中e的地址为: %p\n", &e);
		//throw e;
		BadProcType e("in function divide");
		printf("divide e地址为:%p\n", &e);
		//throw语句后的e会重新分配内存空间,换句话说,从throw语句开始,
		//不再使用divide函数的栈空间,所以表面上只定义了一个e,其实此e非彼e,
		//所以会先调用构造函数,再调用copy构造函数
		printf("before throw e in divide\n");
		throw e;//抛出异常
		//或者写成一下语句,少调用一次copy构造函数
		//throw BadProcType();
	}
//异常2
	else if (y == 1)
	{
		BadProcType e("in function divide");
		throw &e;
	}
	cout << "x/y = " << x / y << endl;
}
void Divide(int x, int y)
{
	BadProcType e("in function Divide");
	printf("Divide x地址为:%p\n", &x);
	printf("Divide y地址为:%p\n", &y);
	divide(x, y);
}






void addr_test() {
	int addr_tool;
	printf("addr_test函数中的addr_tool的地址为:%p\n", &addr_tool);
}
int main()
{
	int addr_tool;
	printf("main函数中addr_tool的地址为:%p\n", &addr_tool);
	try
	{
		cout << "请输入期望发生那种异常,1或2" << endl;
		int i;
		cin >> i;
		if (i == 1)
		{
			Divide(10, 0);
		}
		else
		{
			Divide(100, 1);
		}
	}
	//建议异常抛出的时候以引用的形式接收
//异常1
	catch (BadProcType &e)
	{
		printf("\n catch begin\n");
		addr_test();
		int addr_tool;
		e.flag = "in throw(e)";
		printf("catch 块catch到的e地址为:%p\n", &e);
		printf("catch 块中addr_tools的地址为:%p\n", &addr_tool);
		printf("catch end\n\n");
	}
	//以下代码不建议用
//异常2
	catch (BadProcType * p)
	{
		//根据运行结果可知,析构函数在我们调用p->flag之前已经执行
		//也就是下面的语句在访问已经被析构的对象,这是非常危险的,
		//所以建议以引用的方式传递异常对象
		cout << "这里在访问已经被析构的类,危险: ";
		printf("catch 块 中flag = %s\n", p->flag);
	}
	catch (...)
	{
		cout << "其他未知类型的异常" << endl;
	}
	int b;
	cout << "main 中b的地址为:" << &b << endl;
	system("pause");
}

所以,调用准则是调用throw()函数时,一定要throw对象,而非对象指针,catch时最好使用引用,禁止使用指针,也不推荐使用值传递(上例中未测试)。


自定义异常类型的简单例程

#include <iostream>

using namespace std;

class MyArray
{
public:
	MyArray(int len);
	~MyArray();
	//这里可以只把基类定义露出去
public:
	class eSize;
private:
	class eNegative;
	class eZero;
	class eTooBig;
	class eTooSmall;
	int & operator[](int index);
	int MyArray::getLen();
private:
	int * m_space;
	int m_len;
};

/*--------------------内部类的具体定义----------------*/
class MyArray::eSize//基类
{
public:
	eSize(int size)
	{
		m_size = size;
	}
	virtual void printErr() const = 0 ;
	virtual ~eSize()
	{
	}
protected:
	int m_size;
};
class MyArray::eNegative : public eSize
{
public:
	eNegative(int size) :eSize(size)
	{}

	virtual void printErr() const
	{
		cout << "eNegative 类型异常 size:" << m_size << " ";
	}
};
class MyArray::eZero : public eSize
{
public:
	eZero(int size) :eSize(size)
	{}
	virtual void printErr() const
	{
		cout << "eZero 类型异常 size:" << m_size << " ";
	}
};

class MyArray::eTooBig : public eSize
{
public:
	eTooBig(int size) :eSize(size)
	{}
	virtual void printErr() const
	{
		cout << "eTooBig 类型异常 size:" << m_size << " ";
	}
};
class MyArray::eTooSmall : public eSize
{
public:
	eTooSmall(int size) :eSize(size)
	{}
	virtual void printErr() const
	{
		cout << "eTooSmall 类型异常 size:" << m_size << " ";
	}
};
/*------------------------------------------------------*/

MyArray::MyArray(int len)
{
	if (len < 0)
	{
		throw eNegative(len);
	}
	else if (len = 0)
	{
		throw eZero(len);
	}
	else if (len > 1000)
	{
		throw eTooBig(len);
	}
	else if (len < 3)
	{
		throw eTooSmall(len);
	}
	m_len = len;
	m_space = new int[len];
}

MyArray::~MyArray()
{
	this->m_len = 0;
	delete[] m_space;
}

int & MyArray::operator[](int index)
{
	return m_space[index];
}

int MyArray::getLen()
{
	return m_len;
}




int main()
{
	try
	{
		MyArray a(-5);
	}
	//养成用引用的好习惯
	catch (MyArray::eSize &e)
	{
		e.printErr();
	}
	catch (...)
	{
	}
	system("pause");
	return 0;
}
//不推荐
/*
int main01()
{
	try
	{
		MyArray a(-5);
	}
	catch (MyArray::eNegative)
	{
		cout << "eNegative 类型异常" << endl;
	}
	catch (MyArray::eTooBig)
	{
		cout << "eTooBig 类型异常" << endl;
	}
	catch (MyArray::eZero)
	{
		cout << "eZero 类型异常" << endl;
	}
	catch (MyArray::eTooSmall)
	{
		cout << "eTooSmall 类型异常" << endl;
	}
	catch (...)
	{
	}
	return 0;
}*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值