异常的使用准则
先以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;
}*/