c++ primer plus笔记(11)异常

一、异常机制的构成:

①C++有通过try-throw-catch组成的 异常抛出-捕获-继续运行 的机制:

main()
{
   try{
      ...;
      funct();            //try块中包含可能引发异常的部分
   }
   chatch(Type&)          //捕获Type类型的异常
   {
      ...;                //异常处理语句
   }
}
void funct()              //函数定义
{
   if(...)
      throw (Type&)obj;   //抛出异常Type&类型obj
   ...;
}

//异常机制是错误处理机制,而非错误预测机制。所有可以被捕获的异常一定都是已经被人为预测到的异常。

②异常规范:C++98中,在函数原型后使用throw(Type&)或throw()来指出函数可能抛出或不可能抛出异常:

double funct(double) throw(Type&);   //指出函数可能抛出异常,编译器会检查该块(及块内所有调用)
double funct(double) throw();        //指出函数不可能抛出异常,编译器不检查该块(及块内所有调用)

//C++11中异常规范写法被废弃,而使用noexcept声明:

double funct(double) noexcept;   //指出函数不可能抛出异常,编译器不检查该块

二、异常机制的原理:

①通过try块标记异常的抛出方向(返回到哪里):

main()
{
   try{
      funct1();
      //程序断点1
      funct2();
   }
   catch(Type&)   //异常捕获2
   {
      ...;   //异常处理语句
      //跳转回程序断点1处继续执行
   }
}
funct1()
{
   ...;
   throw (Type&)obj;   //跳转至catch异常捕获2处
}

抛出异常(throw)的方向一定是向着最近一层的try块,此时如果try块后有相应类型的异常捕获(catch),则异常被捕获。

//使用异常类时,可以在throw处直接调用异常类构造函数以创建并抛出异常类对象:

funct1()
{
   ...;
   throw obj(...);   //调用异常类构造函数,创建并抛出异常类对象
}

②通过栈的解退释放异常引发点(throw)直至最近一层的try块间所有的栈内容:

main()
{
   try{
      funct1();
      //程序断点1
      funct2();
   }
   catch(Type&)   //异常捕获2
   {
      ...;   //异常处理语句
      //跳转回程序断点1处继续执行
   }
}
funct1()
{
   funct_f();
   ...;
   throw (Type&)obj;   //跳转至catch异常捕获2处,同时解退funct1()所有栈内容
}
funct_f()
{
   funct_ff();
   ...;
   throw (Type&)obj;   //跳转至catch异常捕获2处,同时解退funct_f()、funct1()期间所有的栈内容
}
funct_ff()
{
   ...;
   throw (Type&)obj;   //跳转至catch异常捕获2处,同时解退funct_ff()、funct_f()、funct1()期间所有的栈内容
}

栈的解退使得程序“倒退执行”;

栈解退期间所有在stack区的指向heap区的指针,也一一被执行delete,相应的heap区内容也被释放。

//与函数返回机制不同,由于抛出的原错误对象已经在throw时被解退,所以catch语句总是创建错误对象的拷贝,即使是使用引用(catch(Type&))。

三、异常类与继承:

①异常类对象的捕获顺序:由于引用(&)的特性,异常基类的引用(&)可以指向所有派生异常类的引用。因此catch时将异常类按派生顺序反向放置,异常基类置于最后:

catch(D_D&)
{
   ...;
}
catch(D&)
{
   ...;
}
catch(B&)
{
   ...;
}

//catch(...)省略号(...)可以指代所有种类的异常,可以捕获任何种类的异常:

catch(D_D&)
{
   ...;
}
catch(D&)
{
   ...;
}
catch(B&)
{
   ...;
}
catch(...)   //捕获所有种类的异常
{
   ...;
}

②库中已经定义的异常基类:

1>头文件<exception>中定义了类exception,常用作派生出用户需要的异常类:

#include<exception>
...
class bad_funct: public std::exception   //从exception派生出用户的异常基类
{
   ...;
}

//exception的名称位于namespace std中。

2>exception类包含virtual成员方法.what(),该方法返回一个(通常用于说明异常类型的)const字符串:

#include<iostream>
#include<exception>
...
class bad_funct: public std::exception   //从exception派生出用户的异常基类
{
public:
   ...;
   const char* what(){return (char*)...;}   //重写继承而来的what()方法
}
main()
{
   try{
      ...;
      funct();
   }
   catch(std::exception& e)   //使用基类引用可以捕获所有派生自基类的派生类
   {
   std::cout << e.what() << std::endl;   //调用what成员方法显示错误类型
   ...;
   }
}
funct()
{
   if(...)
      throw bad_funct(...);   //调用异常类对象构造函数创建并抛出异常
}

③库中继承自exception类的异常类:

头文件<stdexcept>中定义了一些常规程序可能出现的异常,都以异常类的方式被定义,并全部公有继承(public)自基异常类exception:

1>logic_error(逻辑错误):(1)domain_error        //定义域错误(数学)

                                          (2)invalid_argument   //无效参数(函数)

                                          (3)length_error          //长度错误(字符串等)

                                          (4)out_of_bounds      //超界(下标索引)

2>runtiom_error(运行期错误):(1)range_error   //值域错误(数学)

                                                  (2)overflow_error   //上溢错误(浮点超大)

                                                  (3)underflow_error   //下溢错误(浮点超小)

头文件<new>中定义了使用new分配内存错误时,可能引起的异常bad_alloc,该类同样公有继承(public)自基异常类exception。

//bad_alloc类异常无需被手动抛出,在new失败时会自动抛出bad_alloc异常,直接catch即可。

//也可以在new时使用(std::nothrow)改变new为不抛出异常:

type* p = new (std::nothrow)type;   //指出new不抛出异常

此时new失败时返回空指针(NULL)。处理异常时不再使用catch而是使用if判断是否为空指针:

if(NULL == p)

四、两种未被手动处理的异常:

①未捕获异常:没有被catch捕获的异常,可能是由于没有对应的catch。

默认情况下,未捕获异常将使程序调用terminate(),terminate()默认调用abort()或exit(),程序终止:

未捕获异常→terminate()→abort()或exit()→终止。

可以通过set_terminate()函数改变terminate()函数的内容,使其调用用户定义的函数:

void uncaught();
main()
{
   ...;
   set_terminate(uncaught);
   ...;
}
void uncaught()
{
   std::cout << "uncaught exception" << std::endl;
   exit(5);
}

其中set_terminate()原型为:

terminate_handler set_terminate(terminate_handler f)noexcept;

terminate_handler是typedef void(*terminate_handler)()而来,为无参数、无返回值函数指针的typedef。

所以用户定义的可以被terminate()调用的函数,只能是无参数、无返回值的函数。

//未捕获异常无法被抛出,因此无论使用set_terminate调用什么,最终的结果都是程序终止。

②意外异常:没有被异常规范指出的异常。

默认情况下,未捕获异常将使程序调用unexpected(),unexpected()默认调用terminate(),terminate()默认调用abort()或exit(),程序终止:

意外异常→unexpected()→terminate()→abort()或exit()→终止。

可以通过set_terminate()函数改变terminate()函数的内容,使其调用用户定义的函数(同未捕获异常)。

也可以通过set_unexpected()函数改变unexpected()函数的内容,使其调用用户定义的函数:

void uncaught();
main()
{
   ...;
   set_unexpected(uncaught);
   ...;
}
void uncaught()
{
   std::cout << "uncaught exception" << std::endl;
   exit(5);
}

其中set_unexpected()原型为:

unexpected_handler set_unexpected(unexpected_handler f)noexcept;

unexpected_handler是typedef void(*unexpected_handler)()而来,为无参数、无返回值函数指针的typedef。

所以用户定义的可以被unexpected()调用的函数,只能是无参数、无返回值的函数。

但可以被unexpected()调用的函数可以抛出bad_exception异常,可以捕获并处理该异常:

void unexpect() throw(bad_exception);
main()
{
   try{
      ...;
      set_unexpected(unexpect);
      ...;
   }
   catch(std::bad_exception& e)
   {
      std::cout << e.what() << std::endl;
      ...;
   }
}
void unexpect() thorw(bad_exception)
{
   std::cout << "uncaught exception" << std::endl;
   throw bad_exception();
}

unexpected()调用的函数抛出bad_exception异常时需要注意两点:

1>调用的函数规范列表中必须包含bad_exception类异常;

2>bad_exception类也是从基异常类exception类派生而来,因此也有what()成员方法。

//意外异常可以被捕获,最终的结果不一定为程序终止。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值