MySpace

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  18 随笔 :: 2 文章 :: 10 评论 :: 0 Trackbacks

异常可以通过 throw 抛出,比如我定义一个函数别的不做只管抛出异常

先定义一个异常类

class Ex1
{
public:
 string getString()
 {
  return "Ex1 ERROR!";
 }
};

class Ex2:Ex1
{
public:
 string getString()
 {
  return "Ex2 ERROR!";
 }
};

void func1()
{
 Ex1 *e = new Ex2;
 throw e;
}

再定义一个函捕获异常
void func2()
{
 try
 {
  func1();
 }catch(Ex1 e)
 {
   //处理异常
 }
}

 在函数 func1 中的 throw e 代码中,抛出的是不是 Ex2 对象,答案是否定的。在 throw 的时候系统会自动对 e 进行一个拷贝,无论 catch 的时候是取值、引用还是指针,throw 的时候必定要进行一个拷贝,因为要保证当 e 的作用域过了之后这个值还能存在。这个拷贝调用该类的拷贝构造函数。由 e 是这么定义的 Ex1 *e = new Ex2; 那系统调用哪个类的构造函数呢?我们来分析一下,Ex1 是 e 的静态类型,Ex2 是 e 的动态类型。编译器是根据对象的静态类型的来进行拷贝的,调用的是 Ex1 的拷贝构造函数,当然抛出的也是 Ex1 类型的对象。
 在捕获异常的时候,如果这样 catch(Ex1 e) ,那么编译器在此处则会将抛出的异常对象再拷贝一份然后赋值给参数并保存在该异常处理的段中,编译器生成的第一份拷贝在以后会析构掉。如果使用 catch(Ex1& e) 来扑获,系统则将第一次拷贝的引用传递给 e ,这样比上一种方式少了一次拷贝,但是相对同种方式的函数传参方式有多了一次拷贝。传递指针同样如此。
 
 try
 {
  func1();
 }catch(Ex1& e)
 {
   //处理异常
 }catch(Ex2& e)
 {
   //处理异常
 }
 
 从结构上来讲,我感觉跟 switch case 语句差不多但又有所不同,首先 catch 了一场之后不用 break 就能跳出(好象是一句废话)。
再有就是 catch(Ex1& e) 中对传进来的参数进行类型转换的时候跟普通函数是有区别的。它只接受两点:第一,接受向基类方向的继承层次的转换。,第二,接受类型化的指针转换成无类型化的指针。另外如普通函数对参数的类型转换支持 int 到 double 的转换,但是在 catch 中却是不支持此种转换的。

 如果抛出异常的函数写成
 void func1()
 {
  Ex2 &e = new Ex2;
  throw e;
 }
 
 那么对于这段代码
 try
 {
  func1();
 }catch(Ex1& e)
 {
   //处理异常
 }catch(Ex2& e)
 {
   //处理异常
 }
 来说,catch(Ex2& e) 是用于扑获不到该异常的。当异常以传递指针的方式来做的时候,如果第一个catch 为
 catch(void× e)
 的话,那后面的 catch 则就全部成了摆设了。不过有的编译器会对这种写法作出警告信息或是错误信息,我在 VS2005 中就得到了错误信息,编译无法通过。不知道别的编译器会怎么样,这个还没有测试过。

 另外还有个地方需要注意,
 Ex2 e;
 throw e;
 但是在 catch 时为
 catch(Ex1 e)
 由于都是进行传值操作,catch 中转换之后得到 e 是 Ex1 类型的对象,那么这就会产生“切片”问题,e 对象中的虚函数实际上是 Ex1 中的,Ex2 中的成员在 e 中不复存在

posted on 2008-06-19 09:55 yang-chunlei 阅读(216) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理