C++语言学习(十八)——异常处理
2021-07-14 10:08
标签:入口 内存 min copy 申请 main .cpp 兼容 操作符 异常是指程序在运行过程中产生可预料的执行分支。如除0操作,数组访问越界、要打开的文件不存在。 C语言通过setjmp和longjmp对异常处理进行优化。 C++语言中内置了异常处理的语法,try.....catch......。 catch语句块捕获的异常重新解释后可以抛出异常,抛出的异常在外层的try...catch中捕获。 通常在catch语句块中捕获的异常重新解释后可以再次抛出异常,工程实践中通常用于统一异常类型,如通过捕获第三方库函数中抛出的异常,重新解释后抛出统一的异常处理信息。 STL提供了实用的异常处理类,STL中的异常都是从exception类继承而来,exception类只要有两个分支,logic_error和runtime_error。logic_error用于处理程序中可避免逻辑错误,runtime_error用于处理程序中无法处理的恶性错误。 try...catch语句用于分隔正常功能代码与异常处理代码。try...catch语句也可以将函数体分隔为两部分。 上述代码中,func函数声明了抛出的异常类型为int,因此func函数只能抛出int类型异常,如果抛出其它类型异常将导致程序运行终止。即使test函数可以对抛出的其它类型异常进行捕获,程序也会运行终止。 上述代码中,func函数可以抛出多种类型的异常,test函数会捕获func函数抛出的多种异常类型。 如果异常没有被处理,terminate函数会被自动调用。terminate函数是整个程序释放系统资源的最后机会。默认情况下,terminate函数调用abort库函数终止程序。abort函数使得程序执行异常而立即退出。 上述代码运行结果如下: 上述代码在最终terminate_test结束函数中调用了exit(1),exit函数会确保程序中全局、静态数据区的对象被正确销毁。如果使用abort函数替换exit函数,程序运行结果如下: C++语言提供用于声明函数抛出异常的语法声明。异常声明作为函数声明的修饰符,位于函数参数表的后面。函数异常声明的示例如下: 函数异常声明的意义如下: C++编译器不一定对C++语言中函数异常规格说明进行支持。VC++编译器不支持,G++编译器支持。 C语言中,malloc函数申请内存失败时返回NULL值。 上述代码中,自定义new_handler函数,抛出异常时会调用。 上述代码是在G++编译器、VC++编译器下编译执行后打印的结果,表明G++编译器、VC++编译器没有设置默认的new_handler函数。如果C++编译器(如BCC编译器)设置有默认的new_handler函数,func函数执行时将会抛出bad_alloc异常,被捕获后打印出bad_alloc异常的相关信息。 上述代码在执行new操作符函数后会调用Test构造函数,并在初始化m_data成员变量时抛出异常。为了确保不同C++编译器在调用new关键字时具有相同的行为,需要在new失败时不抛出异常,因此需要在new操作符增加函数的异常声明。 上述代码对Test类的new和delete关键字进行了重载,统一了new失败时的行为。 上述代码中使用nothrow关键字对象new进行限制,确保new创建对象失败时不会抛出异常。new关键字也可以指定创建对象的地址空间,比如栈空间。 不是所有的C++编译器都遵循C++标准规范,C++编译器可能重新定义new关键字的实现,并在实现中抛出bad_alloc异常。VC++编译器对new关键字进行了重定义,new关键字在new.cpp文件中进行了实现。 上述代码显示,在new失败时默认抛出bad_alloc异常。 C++语言学习(十八)——异常处理 标签:入口 内存 min copy 申请 main .cpp 兼容 操作符 原文地址:http://blog.51cto.com/9291927/2164586一、C语言异常处理
Bug是指程序中的错误,是不被预期的运行方式。如野指针、堆空间使用结束未释放。
C语言中处理异常的方式一般是使用if....else...分支语句。double divide(double a, double b)
{
const double delta = 0.000000000000001;
double ret = 0;
if( !((-delta
int setjmp(jmp_buf env);
将上下文保存到jmp_buf结构体中void longjmp(jmp_buf env, int value);
从jmp_buf结构体中恢复setjmp保存的上下文,最终从setjmp函数调用点返回,返回值为value。#include
二、C++语言异常处理机制
1、异常处理简介
try语句块用来处理正常代码逻辑,catch语句块用来处理异常处理情况,throw抛出异常。在try语句块抛出的异常在相应的catch语句块捕获处理。
同一个try语句块可以对应多个catch语句块,catch语句块可以定义具体处理的异常类型,不同的类型的异常由不同的catch语句块处理,try语句块可以抛出任何类型的异常,catch(...)用于处理所有类型的异常,任何异常都只能被捕获一次。
throw抛出的异常必须被catch处理,如果当前函数能够处理异常,继续执行;如果当前函数不能处理异常,函数停止执行并返回。未被处理的异常会顺着函数调用栈向上传递,直到被处理为止,否则程序将停止执行。
异常处理的匹配:
A、异常抛出后从上到下严格匹配每个catch语句块处理的类型,不能进行任何类型转换。
B、catch(...)语句块只能放到catch语句块分支的最后位置。
异常处理的使用实例: try
{
throw ‘c‘;
}
catch(char c)
{
cout
try
{
try
{
throw ‘c‘;
}
catch(int i)
{
cout
异常的类型可以是自定义类型,自定义类型的异常匹配依旧是自上而下严格匹配,但由于赋值兼容性原则在异常匹配中适用,所以匹配子类异常的catch语句块放在catch分支的上部,匹配父类异常的catch语句块放在catch分支的下部。#include
2、STL中的异常处理
#include
3、try...catch特殊语法
函数声明和定义时可以直接指定可能抛出的异常类型,异常声明作为函数的一部分可以提高代码可读性。
函数异常声明是一种与编译器之间的契约,函数声明异常后就只能抛出声明的异常。如果抛出其它异常将会导致程序运行终止。也可以通过函数异常声明定义无异常函数。#include
如果函数内部可能会抛出多种类型的异常,需要在函数声明异常时指定声明的异常类型,代码如下:#include
4、未被处理的异常
#include
C++支持使用自定义的terminate函数实现替换默认的terminate函数实现。
自定义terminate函数的实现规则如下:
A、自定义一个无返回值、无参数的函数
B、不能抛出任何异常
C、必须以某种方式结束当前程序
通过调用set_terminate函数可以设置自定义的terminate结束函数,其用法如下:
A、参数类型为void (*)()
B、返回值为默认的terminate函数入口地址#include
析构函数中抛出异常可能会导致最终结束函数terminate函数会被重复调用。5、函数的异常规格说明
//可能抛出任何异常
void func1();
//只能抛出的异常类型:char,int
void func2() throw(char, int);
//不抛出任何异常
void func3() throw();
A、提示函数调用者必须做好异常处理的准备
B、提示函数的维护者不要抛出其它异常
C、函数异常规格说明是函数接口的一部分
如果函数抛出的异常类型不在函数异常声明中,全局unexpected()函数会被调用。默认的unexpected()函数会调用全局的terminate函数,可以自定义函数替换默认的unexpected()函数实现。
自定义的unexpected()函数的实现规则如下:
A、自定义一个无返回值、无参数的函数
B、能够再次抛出异常,当异常符合触发函数的异常规格说明时,恢复程序执行。否则,调用全局terminate函数结束程序。
通过调用set_unexpected函数可以设置自定义的unexpected()函数,用法如下:
A、参数类型为void (*)()
B、返回值为默认的unexpected()函数入口地址。#include
6、动态内存申请异常
C++语言中,对于早期的C++编译器,new关键字申请内存失败时,返回NULL值;对于现代C++编译器,new关键字申请内存失败时,抛出std::bad_alloc异常。
C++语言规范中,new关键字的标准行为如下:
A、new在内存分配时,如果空间不足,会调用全局的new_handler函数,new_handler函数中抛出std::bad_alloc异常;如果成功,会在分配的空间调用构造函数创建对象,并返回对象的地址。
B、可以自定义new_handler函数,处理默认new内存分配失败的情况。#include
#include
不同的C++编译器,new关键字申请动态内存失败时表现不同。
工程实践中,为了在不同C++编译器间统一new关键字的行为,提高代码的可移植性,解决方案如下:
A、重新定义全局的new/delete实现,不抛出任何异常;自定义new_handler函数,不抛出任何异常(不推荐)。
B、在类内重载new/delete操作符,不抛出任何异常。
C、单次动态内存分配时使用nothrow参数,指明new不抛出异常。#include
#include
#include
7、C++编译器对new关键字的实现
#ifdef _SYSCRT
#include
上一篇:14、python 日期和时间
下一篇:Ajax 与Json
文章标题:C++语言学习(十八)——异常处理
文章链接:http://soscw.com/index.php/essay/105067.html