C++中的异常处理

常见的异常处理有:new无法取得所需内存、数组下标超界、运算溢出、除数为0和无效函数参数。
异常处理特别适合用于程序无法恢复但又需要提供有序清理,以使程序可以正常结束时。

良好编程习惯:发生范围与处理范围不同的错误可以使用异常处理。发生范围与处理范围相同的错误,则应该用其他方法处理。
良好编程习惯:使用异常处理时,尽量避免错误处理以外的其他用途的处理,这样可使程序更清晰。

异常处理用于错误处理,不是用于处理程序准备终止时常有的活动。

性能提示:尽管异常处理可以进行错误处理以外的工作,但将两种用途混用会降低性能。
性能提示:编译器实现异常处理时,通常会在异常不发生时将异常处理代码开销降到极小或为0,发生异常时,则执行有关的系统开销。当然,异常处理代码无凝会使程序占用更多内存。

软件工程知识:传统控制结构的控制流通常比使用了异常的控制流更清晰、更有效。

常见编号错误:传统程序控制使用异常处理的另一危险是堆栈解退,异常发生之前分配的资源可能无法释放。这个问题可以通过认真编程来避免。

软件工程知识:异常处理适用于分组件开发的系统。异常处理使组件组合更为容易。每个组件可以独立于其他异常处理范围之外,执行自己的异常检测。

良好编程习惯:对程序本身容易处理的简单而又局部性错误,应用传统错误处理方法而避免使用异常处理。

软件工程知识:对于库操作,库函数调用都通常用特定错误处理方法处理库函数中产生的异常。库函数很难进行可满足用户独特需求的错误处理。所以我们说异常适合处理库函数产生的错误。

一些错误处理方法总结如下:
1>用assert测试编码和设计错误,如果返回false,则程序终止,改正代码。这样的方法非常适用于调试。
2>测试错误条件、发出错误消息和调用exit,向程序球境传递相应的错误代码。
3>setjump和longjump。这些<csetjmp.h>提供的库函数,可以指定从深层嵌套函数立即转入错误处理器。如果没有setjump/longjump,程序要执行几层返回才能从深层嵌套函数退出。这中方法可以转入某个错误处理程序。但这种方法在C++中有潜在危险,因为它解退堆栈时不调用自动对象的析构函数,可能会造成严重问题。
4>某些特定错误有专门的处理功能。例如,new无法分配内存时,可以用new_handler函数处理错误。通过提供函数外作为set_new_handler的参数可以改变这个函数。

常见编程错误:退出程序会使其他程序无法使用其资源,从而造成资源泄漏。

软件工程知识:异常处理的关键是程序或系统中处理异常的部分可以与检测产生异常的部分完成不同或有明显差异。

除数为0的异常处理例子:

#include <iostream>
using namespace std;

class DivideByZeroException
{
public:
 DivideByZeroException():message("attempted to divide by zero") {}
 const char *what() const
 {
  return message;
 }
private:
 const char *message;
};

double quotient( int numerator, int denominator )
{
 if ( denominator == 0 )
  throw DivideByZeroException();

 return static_cast< double >( numerator) / denominator;
}

int main()
{
 int number1, number2;
 double result;

 cout << "Enter two integers (end-of-file to end):";
  
 while ( cin >> number1 >> number2 )
 {
  try
  {
   result = quotient( number1, number2 );
   cout << "The quotient is:" << result << endl;
  }
  catch ( DivideByZeroException ex )
  {
   cout << "Exception occurred:" << ex.what() << '/n';
  }

  cout << "/nEnter two integers (end-of-file to end):";
 }

 cout << endl;

 system("pause");

 return 0;
}
良好编程习惯:将各种执行时错误与相应的命名异常对象关联,可使程序更清晰。

软件工程知识:如果需要传递导致异常的错误信息,可以把此类信息放入抛出对象。catch处理程序包含引用此类信息的参数名。
软件工程知识:抛出异常对象时也可以不传递信息,此时只需知道抛出这种类型的对已提供了异常处理程序成功完成工作所需的足够信息。

常见编程错误:异常只能在try块中抛出,如果在try块外部抛出异常,可能会调用terminate。
常见编程错误:可能会抛出条件异常。但一定要小心使用,因为提升规则可能使条件表达式返回的值不是需要的类型。例如,从同一条件表达式抛出int成double时,条件表达式int变成double。因此,结果总是由参数为double的处理程序捕捉,而不是有时由参数为double的处理程序捕捉,有时由参数为int的处理程序捕捉。
常见编程错误:指定用返号分开的catch参数是语法错误。
        catch后跟括号和省略号         catch(...) 表示捕捉所有异常。
常见编程错误:将catch(...)放在其他catch块之前时,其他块根本无法执行。try块之后的处理程序列表中catch(...)总是最后一个。
软件工程知识:用catch(...)捕捉异常有两个缺点;其一是始终无法确定异常类型;其二是没有命名参数,就无法在异常处理程序中引用异常对象。

常见编程错误:将对基类类型异常的“捕捉”放在对派生类类型的“捕捉”之前是逻辑错误。基类类型catch会捕捉所有从该类派生出来的所有对象,因此不会执行派生类类型的catch。

测试和调试提示:程序员需要确定异常处理程序列出的顺序。这个顺序可能影响try块中所产生异常的处理方法。如果程序处理异常时出现意外行为,可能是前面的catch块捕获处理了这个异常,使其没有被指定的异常处理程序处理。

下列情况下,catch处理程序参数类型和所抛出对象的类型匹配:
   实际是同一类型;
   catch处理程序参数类型是所抛出对象类的public基类;
   处理程序参数为基类指针或引用类型,而抛出对象为派生类指针或引用类型;
   catch处理器为catch(...)。

常见编程错误:将带void *参数类型的异常处理程序放在具有其他指针类型的异常处理程序之前是逻辑错误。void *处理程序捕捉所有指针类型的异常,因些根本不可能执行其他异常处理程序。

常见编程错误:将分号放在try块之后或try块之后的任何catch处理程序(最后一个catch处理程序除外)之后是语法错误。

软件工程知识:最好在设计过程中把异常处理策略加入系统,因为在系统实施后再加入有效的异常处理策略很困难。

常见编程错误:认为处理异常后控制会返回抛出点之后第一条语句是逻辑错误。

软件工程知识:传统控制流程不用异常的另一个原因是这些“额外增加的”异常可能打乱真正的错误型异常。程序员更难跟踪异常种类。例如,程序处理大量异常时,如何确定catch(...)捕捉的异常呢?异常情况只能是较为少见的,不常发生的情况。

throw;   这种不带参数的throw用于重抛出异常。如果开始不抛出异常,重抛出异常就要调用terminate。

处理new失败:
C++标准指定,出现new失败时,将抛出bad_alloc异常(在头文件<new>中定义)。但许多编译器目前还不支持该标准,仍然会在new失败时返回0。

#include <iostream>
using namespace std;

int main()
{
 double *Ptr[ 50 ];

 for ( int i = 0; i < 50; i ++ )
 {
  Ptr[ i ] = new double[ 5000000 ];

  if ( Ptr[i] == 0 )
  {
   cout << "Memory allocation failed for Ptr[" << i << "]/n";
   break;
  }
  else
  {
   cout << "Allocation 5000000 doubles in Ptr[" << i << "]/n";
  }
 }

 return 0;
}
不同系统的输出可能不同,具体情况与实际内存、可用虚拟内存的磁盘空间和用于编译程序的编译器有关。

#include <iostream>
#include <new>
using namespace std;

int main()
{
 double *Ptr[ 50 ];

 try
 {
  for ( int i = 0; i < 50; i ++ )
     {
      Ptr[ i ] = new double[ 5000000 ];
   cout << "Memory allocation failed for Ptr[" << i << "]/n";
     }
 }

 catch ( bad_alloc exception)
 {
  cout << "Exception occurred:"
   << exception.what() << endl;
 }

 return 0;
}
C++标准指定标准支持的编译器在new失败时仍然可以用返回0的版本。为此头文件<new>事实上义类型nothrow,使用语句
             double * ptr = new (nothrow) double[ 5000000 ];
该语句表示用不支持抛出bac_alloc异常的new版本(即nothrow)分配5000000个double值的数组。

软件工程知识:为了使程序更健壮,C++标准建议程序员使用抛出bad_alloc异常的new版本。

软件工程知识:标准的exception层次结构只是一个起点,用户可以抛出标准异常、抛出从标准异常派生的异常或抛出非标准异常派生的异常。

常见编程错误:用户自定义的异常类无须从exception类派生。所以编写catch(exception e)并不能保证捕捉程序可能遇到的所有异常。

测试和调试提示:要捕捉try块可能抛出的所有异常,应用catch(...)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值