[转]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表达式

posted @ 2009-03-27 15:08  andriod2012  阅读(122)  评论(0编辑  收藏  举报