#include
#include
using namespace std; int test(int i) { if( i == 1 ) { throw -1; } if( i == 2 ) { throw "ERROR"; } if( i == 3 ) { throw 0.5; } if( i == 4 ) { throw 'd'; } return i; } int main(int argc, char *argv[]) { for(int i=0; i<4; i++) { try { cout<
注意:第一,catch后面可以只有类型,没有具体变量,但是当没有变量的时候,catch语句块中就不能获得throw语句抛出的异常参数了! 第二,对于这样一个try语句块和多个catch语句块的时候,基本数据类型往往是不够用的,所以尽量使用自己通过class关键字定义的类类型!
3.深入异常处理:
a.c++中的catch语句可以使用...捕获所有类型的异常。catch(...)虽然可以捕获所有类型的异常但却无法得到异常信息。catch(...)一般作为最后一个异常处理块出现。看见代码中的catch就要意识到这里在处理异常情况,而异常是在对应的try中产生的。 b.在catch语句块中,仍然可以使用throw抛出异常 c.
在catch语句块中,可以使用throw a , 也可以只使用throw; 当throw后面没有变量也没有数据的时候,是表示要抛出catch语句块接受的异常类型(即在catch(int a)这个语句块中,throw; 就是throw a; 的简写)。所以throw; 这种情况只能在catch语句块中使用,不能在try语句块中使用。同时即使是catch(int)这样的语句块中,使用throw; 在外层的catch(int e)异常处理中,也可以获得e的值,就是最初的那个throw 抛出的值!应该是编译器自动提供了一个中间变量! d.
注意在,catch(...)中的throw; 可以直接把接收到的异常再次抛出!应该也是编译器自动提供了中间变量! 示例代码:
#include
#include
using namespace std; int test(int i) { if( i == 1 ) { throw -1; } if( i == 2 ) { throw "ERROR"; } if( i == 3 ) { throw 0.5; } if( i == 4 ) { throw 'd'; } return i; } int main(int argc, char *argv[]) { for(int i=0; i<5; i++) { try { try { cout<
4.异常与对象:
a.
切记,千万不要在构造函数中抛出异常,构造函数中可能会申请系统资源(如new int[5]),而在构造函数中抛出异常则会导致对象构造不完全,对于不完全对象的析构函数是不会被调用的,因此可能会造成资源泄漏!(即析构函数不被调用,析构函数中的delet [] 没有执行,造成内存泄漏) 示例代码:
#include
using namespace std;
class test
{
public:
test()
{
cout << "test()....." << endl;
throw 'a';
}
~test()
{
cout << "~test()....." << endl;
}
};
void fun()
{
try
{
test t1;
}
catch(char e)
{
cout << e << endl;
}
}
int main()
{
fun();
return 0;
}
注意:上面的对象因为是构造不完全的对象,所以对象的析构函数不被调用!
5.工程中的异常应用:
a.在工程中会定义一系列的异常类,通过继承可以得到一个异常类族。每个类代表工程中可能出现的一种异常类型! b.
在工程中可以使用标准库中的异常类,可以将标准库中的异常类作为基类派生新的异常类! c.
标准库中的异常都是从exception类派生而来的,exception类有两个主要分支,logic_error用于描述程序中出现的逻辑错误,如:传递无效参数。runtime_error用于描述无法预料的事件所造成的错误,如:内存耗尽、硬件错误等。 标准库中的异常如图:
d.标准库中的异常族包含在
头文件中! e.
logic_error和runtime_error都提供了一个参数为字符串的构造函数,这样就能够保存异常信息,还可以通过what()成员函数得到异常信息!!! 示例代码:
#include
#include
//异常族的头文件 using namespace std; double Div(double a, double b) { //invalid_argument p("Divide by zero..."); if( (-0.00000001 < b) && ( b < 0.00000001) ) { /* invalid_argument是一个异常类,invalid_argument("Divide by zero...") 是直接调用这个类的构造函数(后面是构造函数的参数,也是异常信息), 此时编译器产生一个临时对象,throw就是抛出了这个临时对象 */ throw invalid_argument("Divide by zero..."); //throw p; } return a / b; } int main() { try { cout<
注意:在catch语句后,可以使用引用参数,使用引用就避免了对象的构造和拷贝的开销,效率会高些!
使用自己创建的异常对象示例代码:
#include
#include
//异常族的头文件 using namespace std; class div_zero_error : public logic_error { public: div_zero_error(const char* s) : logic_error(s)//初始化列表~~~ { } }; double Div(double a, double b) { //div_zero_error p("Divide by zero..."); if( (-0.00000001 < b) && ( b < 0.00000001) ) { /* div_zero_error是一个异常类,div_zero_error("Divide by zero...") 是直接调用这个类的构造函数(后面是构造函数的参数,也是异常信息), 此时编译器产生一个临时对象,throw就是抛出了这个临时对象 */ throw div_zero_error("Divide by zero..."); //throw p; } return a / b; } int main() { try { cout<
注意:
第一,当希望通过标准库中的异常类族派生出自己的异常类的时候,首先要自己定义类并继承基类,这样自己的异常类中就继承了logic_error类的构造函数,这个带参的构造函数(即参数是字符串,用来保存异常信息的)是对应what()成员函数的。所以就出现了这样的语句,div_zero_error(const char* s) : logic_error(s) 这条语句很有意思,这条语句使用了构造函数的初始化参数列表!当创建div_zero_error类对象的时候,先接收构造函数的参数,且保存在s中,不管是从初始化列表的角度看,还是从父类构造函数的角度看,都是先调用logic_error类的构造函数,再调用div_zero_error类的构造函数,此时在调用logic_error类的构造函数的时候,就完成了对异常信息的保存!!!等于说把子类div_zero_error的构造函数的参数赋值给了父类logic_error的构造函数的参数,用于传递异常信息,即div_zero_error的构造函数的参数就是用来接收异常信息的!对于那些标准库中的其他子类,原理依然相同!
第二,在throw语句中,throw后面的对象,不管是临时对象,还是事先创建好的对象,这个对象在创建的时候一定是带字符串参数的,如:div_zero_error p("Divide by zero...");和div_zero_error("Divide by zero..."); 没有参数是编译不过的(这个参数就是异常信息),因为在创建异常类对象的时候,是需要调用父类logic_error的带字符串参数的构造函数的,所以会报错!当然对于属于标准库中的异常类,依然需要准守这个规则!因为标准库中的异常类的创建过程,跟自己定义的异常类创建的过程是一模一样的!!!只不过一个是库函数帮你写好的,一个是你自己写的,代码是相同的!
第三,对于工程中的代码,如果已经约定好所有的异常都是基于exception这个基类派生的异常类族,当想接收所有异常的时候,就不用catch(...)这样的语句了,因为catch(...)不能获得what函数的异常信息!可以使用catch(exception& error)这样的语句来捕获所有异常,这里利用的是子类父类之间的赋值兼容性原则(即子类是特殊的父类,exception类可以接收所有他的子类)!
6.函数级try语法:
注意:其实两段代码没有任何区别,仅仅是多一对括号和少一对括号的区别!右侧的代码主要是,怕try语句块中的代码过多,使程序员忘记这是在try语句块中!仅仅是为了提高代码的可读性和维护性!意义并不是很大,遇到了能够认识就可以了!
c++学习笔记(18.异常处理)(二)
出后会自上而下逐一匹配catch语句块,异常匹配时,不会进行默认类型转换! 示例代码: