[转]C++异常处理 2
}
}
void main(){
ExceptionClass e("Test");
try{
e.mythrow();
}
catch(...)
{
cout<<”*********”<<endl;
}
}
这是输出信息:
Construct Test
Construct my throw
Destruct my throw
****************
Destruct my throw (这里是异常处理空间中对异常类的拷贝的析构)
Destruct Test
======================================
不过一般来说我们可能更习惯于把会产生异常的语句和要throw的异常类分成不同的类来写,下面的代码可以是我们更愿意书写的:
………..
class ExceptionClass{
public:
ExceptionClass(const char* name="Exception Default Class"){
cout<<"Exception Class Construct String"<<endl;
}
~ExceptionClass(){
cout<<"Exception Class Destruct String"<<endl;
}
void ReportError() {
cout<<"Exception Class:: This is Report Error Message"<<endl;
}
};
class ArguClass{
char* name;
public:
ArguClass(char* name="default name"){
cout<<"Construct String::"<<name<<endl;
this->name=name;
}
~ArguClass(){
cout<<"Destruct String::"<<name<<endl;
}
void mythrow(){
throw ExceptionClass("my throw");
}
};
_tmain()
{
ArguClass e("haha");
try {
e.mythrow();
}
catch(int)
{
cout<<"If This is Message display screen, This is a Error!!"<<endl;
}
catch(ExceptionClass pTest)
{
pTest.ReportError();
}
catch(...){
cout<<"***************"<<endl;
}
}
输出Message:
Construct String::haha
Exception Class Construct String
Exception Class Destruct String
Exception Class:: This is Report Error Message
Exception Class Destruct String
Destruct String::haha
使用异常规格编程
如果我们调用别人的函数,里面有异常抛出,用去查看它的源代码去看看都有什么异常抛出吗?这样就会很烦琐。比较好的解决办法,是编写带有异常抛出的函数时,采用异常规格说明,使我们看到函数声明就知道有哪些异常出现。
异常规格说明大体上为以下格式:
void ExceptionFunction(argument…) throw(ExceptionClass1, ExceptionClass2, ….)
所有异常类都在函数末尾的throw()的括号中得以说明了,这样,对于函数调用者来说,是一清二楚的。
注意下面一种形式:
void ExceptionFunction(argument…) throw()
表明没有任何异常抛出。
而正常的void ExceptionFunction(argument…)则表示:可能抛出任何一种异常,当然,也可能没有异常,意义是最广泛的。
异常捕获之后,可以再次抛出,就用一个不带任何参数的throw语句就可以了。
构造和析构中的异常抛出
这是异常处理中最要注意的地方了
先看个程序,假如我在构造函数的地方抛出异常,这个类的析构会被调用吗?可如果不调用,那类里的东西岂不是不能被释放了?
#include <iostream.h>
#include <stdlib.h>
class ExceptionClass1
{
char* s;
public:
ExceptionClass1(){
cout<<"ExceptionClass1()"<<endl;
s=new char[4];
cout<<"throw a exception"<<endl;
throw 18;
}
~ExceptionClass1(){
cout<<"~ExceptionClass1()"<<endl;
delete[] s;
}
};
void main(){
try{
ExceptionClass1 e;
}catch(...)
{}
}
结果为:
ExceptionClass1()
throw a exception
在这两句输出之间,我们已经给S分配了内存,但内存没有被释放(因为它是在析构函数中释放的)。应该说这符合实际现象,因为对象没有完整构造。
为了避免这种情况,我想你也许会说:应避免对象通过本身的构造函数涉及到异常抛出。即:既不在构造函数中出现异常抛出,也不应在构造函数调用的一切东西中出现异常抛出。
但是在C++中可以在构造函数中抛出异常,经典的解决方案是使用STL的标准类auto_ptr。
其实我们也可以这样做来实现:在类中增加一个 Init(); 以及 UnInit();成员函数用于进行容易产生错误的资源分配工作,而真正的构造函数中先将所有成员置为NULL,然后调用 Init(); 并判断其返回值/或者捕捉 Init()抛出的异常,如果Init();失败了,则在构造函数中调用 UnInit(); 并设置一个标志位表明构造失败。UnInit()中按照成员是否为NULL进行资源的释放工作。
那么,在析构函数中的情况呢?我们已经知道,异常抛出之后,就要调用本身的析构函数,如果这析构函数中还有异常抛出的话,则已存在的异常尚未被捕获,会导致异常捕捉不到。
标准C++异常类
C++有自己的标准的异常类。
① 一个基类:
exception 是所有C++异常的基类。
class exception {
public:
exception() throw();
exception(const exception& rhs) throw();
exception& operator=(const exception& rhs) throw();
virtual ~exception() throw();
virtual const char *what() const throw();
};
② 下面派生了两个异常类:
logic_erro 报告程序的逻辑错误,可在程序执行前被检测到。
runtime_erro 报告程序运行时的错误,只有在运行的时候才能检测到。
以上两个又分别有自己的派生类:
③ 由logic_erro派生的异常类
domain_error 报告违反了前置条件
invalid_argument 指出函数的一个无效参数
length_error 指出有一个产生超过NPOS长度的对象的企图(NPOS为size_t的最大可表现值
out_of_range 报告参数越界
bad_cast 在运行时类型识别中有一个无效的dynamic_cast表达式