C++内置了异常处理语法元素 try ,catch,throw。
try里面语句里产生异常( devide(1,0)产生除零异常),try里面函数devide用throw扔出异常值(对象,值),然后程序返回调用点,catch在调用点捕获到异常。
try { double r = divide(1, 0); //运行包含异常的代码 } catch(...) { cout << "error" << endl; //上面语句有异常则调用这里的语句 }
throw抛出的异常必须被catch处理。当前函数能够处理异常,则程序往下执行。当前函数无法处理异常,函数停止执行并异常返回(没有返回值)。
没有被处理的异常顺着函数调用栈向上传播,直至异常被处理,如果没有处理异常则整个程序停止。
例:
在函数3中发生异常,用关键字throw抛出异常给try,try再传递异常给catch。如果没有try和catch这样异常处理语句,throw则返回异常值1给上一个函数2,
函数2,1也做同样的工作,如果找不到异常处理语句,程序停止在函数1。
除零异常:
#include <iostream> #include <string> using namespace std; double divide(double a, double b) { if( b == 0) { throw 0; } else { return a / b; } } int main(int argc, char *argv[]) { try { divide(1, 0); } catch(...) { cout << "error" << endl; } return 0; }
规则:
一个try语句块可以跟上多个catch语句。
catch语句可以定义具体处理的异常类型。
不同类型是我异常由不同的catch语句负责。
try语句可以抛出任何类型的异常。
catch(...)用于处理任意类型的异常。
任何异常都只能被捕获(catch)一次 。
异常处理的匹配规则:
异常由throw抛出异常值后,由try传给catch,再由每个catch严格匹配异常值类型(不会进行类型转化),如果不匹配则调用下一个catch,如果程序没有匹配的catch则停止运行。
#include <iostream> #include <string> using namespace std; void Demo1() { try { throw 'c'; } catch(char c) { cout << "catch(char c)" << endl; // 不进行类型转化,一个异常只能被一个catch所捕获 } catch(short c) { cout << "catch(short c)" << endl; // 不进行类型转化 } catch(double c) { cout << "catch(double c)" << endl; // 不进行类型转化 } catch(...) { cout << "catch(...)" << endl; // 用于接收任意类型的异常,只能放在所有catch最后一个的位置 } } int main(int argc, char *argv[]) { Demo1(); // catch(...) return 0; }
catch相当于函数的调用,参数严格匹配的那种
catch语句也可以抛出异常:
try // 外层异常处理语句 { try // 内层异常处理语句 { throw 1; } catch(int i) // 内层catch中捕获异常值为int类型后处理异常或抛出异常 { throw i; } catch(...) // 内层catch捕获任意异常后处理或则抛出 { throw; } } catch(int i) //外层catch中捕获异常后处理内层异常或再抛出异常 { }
catch抛出异常的意义:统一异常类型。
当用私有库封装第三方库时,第三方库的函数func()的异常类型为short,对应的封装函数为my_func()的异常类行为int,可以在func()中抛出short类型的异常值,在函数my_func()中用catch接住异常并重新抛出int类型的异常值,这样就可以统一异常值类型了。
示例如下:
#include <iostream> #include <string> using namespace std; void lib_func(int i) // 三方库函数 { try { throw i; } catch(int i) { if(i < 0) throw -1; // 不做处理,直接抛出异常 if(i > 0) throw 1; if(i == 0) throw 0; } } void my_func(int i) // 自己库函数 { try { lib_func(i); } catch(int i) { switch(i) // 对于异常的重新解释 { case 1: throw "Timeout Exception"; break; case 0: throw "Runtime Exception"; break; case -1: throw "Invalid Parameter"; break; } } } int main(int argc, char *argv[]) { try { my_func(0); } catch(const char* cs) { cout << "Exception Info: " << cs << endl; } return 0; }
类类型的异常值:
自上而下严格匹配,子类的异常对象可以被父类catch语句块所抓住(子类初始化父类)(赋值兼容性原则)。匹配子类异常的catch放在上面,匹配父类异常的catch放在下面,防止子类被父类捕获。
用自定义类类型来描述异常,实现如下:
#include <iostream> #include <string> using namespace std; class Base { }; class Exception : public Base //自定义异常类 { public: int m_id; string m_desc; Exception(int id, string desc) { m_id = id; m_desc = desc; } }; void lib_func(int i) // 三方库函数 { try { throw i; } catch(int i) { if(i < 0) throw -1; if(i > 0) throw 1; if(i == 0) throw 0; } } void my_func(int i) // 自己的库函数 { try { lib_func(i); } catch(int i) { switch(i) // 对于三方库异常的重新解释 { case 1: throw Exception(0,"Timeout Exception"); break; case 0: throw Exception(1,"Runtime Exception"); break; case -1: throw Exception(-1,"Invalid Parameter"); break; } } } int main(int argc, char *argv[]) { try { my_func(0); } catch(const Exception& e) // 打印异常类的信息。使用引用,防止调用拷贝构造函数 { cout << "Exception Info: " << " ID: " << e.m_id << " Description: " << e.m_desc << endl; } catch(const Base& e) // 子类可以初始化父类,这里也可以调用 { cout << "catch(const Base& e)" << endl; } return 0; }
C++标准库含有异常类族
exception类,logic_error类,runtime_error类
logic_error:可以避免的错误(空指针,下标越界)
runtimer_error:无法避免的错误(内存溢出)
main()函数中抛出异常:
主函数中有无法处理的异常时,terminate()函数会被自动调用,terminate()继续调用abort()函数中止程序并异常退出,C++支持替换默认的terminate()函数。
terminate()替换函数:
1. 定义一个无参无返回值的函数:
2. 不能抛出任何异常
3. 结束当前进程
调用set_terminate定义terminate()替换函数:
1. 参数类型为void(*)()
2. 返回值为默认的termainate()函数入口地址
#include <iostream> #include <cstdlib> #include <exception> // 异常处理头文件 using namespace std; void my_terminate() { cout << "my_terminate()" << endl; exit(1); // 结束当前进程 } class Test { public: Test() { cout << "Test()"; cout << endl; } ~Test() { cout << "~Test()"; cout << endl; } }; int main() { set_terminate(my_terminate); // 设置自定义中止函数 static Test t; throw 1; return 0; }
异常声明:
声明函数所抛出的异常。异常声明时函数声明的修饰符,写在参数列表后面。异常规格说明是函数接口的一部分。
void func(); // 可能抛出任何异常
void func(); throw(int, char); // 可能抛出的异常是int类型的异常值或char类型的异常值
void func(); throw(); // 不抛出任何异常
函数抛出的异常值类型不在异常说明里面时,全局函数unexcepected()函数被调用,然后默认的unexcepected()函数会调用全局terminate()函数。(可用自定义的函数替换unexcepected()函数)
unexcepected()函数的替换:
1. 能够再次抛出异常,当异常符合触发函数的异常规格说明时,程序恢复执行。否则调用全局terminate()结束进程。
2. 调用set_unexcepected()函数设置自定义的unexcepected()函数。
3. 自定义的unexcepected()函数参数类型是void(*)(),返回值是unexcepected()函数的入口。
#include <iostream> #include <cstdlib> #include <exception> using namespace std; void my_unexpected() { // exit(1); throw 1; } void func() throw(int) { throw 'c'; // 抛出的异常不符合异常规格,将调用自定义的my_unexpected函数, } // 在my_unexpected函数中又抛出为int类型异常值,异常值兼容异常规格,程序恢复执行。 int main() { set_unexpected(my_unexpected); try { func(); // 相当于只发生了int的异常 } catch(int) // 可以只声明了异常的类型,并没有使用标识符来表示捕获异常 { cout << endl; } catch(char) { cout << endl; } return 0; }