本篇文章给大家分享的是有关C++中怎么使用trythrowcatch进行异常处理,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。
C++异常处理基本语法
C++ 通过 throw 语句和 try…catch 语句实现对异常的处理。throw 语句的语法如下:
throw 表达式;
该语句拋出一个异常。异常是一个表达式,其值的类型可以是基本类型,也可以是类。
try…catch 语句的语法如下:
try { 语句组}catch(异常类型) { 异常处理代码}...catch(异常类型) { 异常处理代码}
catch 可以有多个,但至少要有一个。
不妨把 try 和其后{}中的内容称作“try块”,把 catch 和其后{}中的内容称作“catch块”。
try…catch 语句的执行过程是:
执行 try 块中的语句,如果执行的过程中没有异常拋出,那么执行完后就执行最后一个 catch 块后面的语句,所有 catch 块中的语句都不会被执行; 如果 try 块执行的过程中拋出了异常,那么拋出异常后立即跳转到第一个“异常类型”和拋出的异常类型匹配的 catch 块中执行(称作异常被该 catch 块“捕获”),执行完后再跳转到最后一个 catch 块后面继续执行。
例如下面的程序:
#include <iostream>using namespace std;int main(){ double m ,n; cin >> m >> n; try { cout << "before piding." << endl; if( n == 0) throw -1; //抛出int类型异常 else cout << m / n << endl; cout << "after piding." << endl; } catch(double d) { cout << "catch(double) " << d << endl; } catch(int e) { cout << "catch(int) " << e << endl; } cout << "finished" << endl; return 0;}
程序的运行结果如下:
9 6↙before piding.1.5after piding.finished
说明当 n 不为 0 时,try 块中不会拋出异常。因此程序在 try 块正常执行完后,越过所有的 catch 块继续执行,catch 块一个也不会执行。
程序的运行结果也可能如下:
9 0↙before piding.catch\(int) -1finished
当 n 为 0 时,try 块中会拋出一个整型异常。拋出异常后,try 块立即停止执行。该整型异常会被类型匹配的第一个 catch 块捕获,即进入 catch(int e) 块执行,该 catch 块执行完毕后,程序继续往后执行,直到正常结束。
如果拋出的异常没有被 catch 块捕获,例如,将catch(int e),改为catch(char e),当输入的 n 为 0 时,拋出的整型异常就没有 catch 块能捕获,这个异常也就得不到处理,那么程序就会立即中止,try…catch 后面的内容都不会被执行。
能够捕获任何异常的 catch 语句
如果希望不论拋出哪种类型的异常都能捕获,可以编写如下 catch 块:
catch(...) { ...}
这样的 catch 块能够捕获任何还没有被捕获的异常。例如下面的程序:
#include <iostream>using namespace std;int main(){ double m, n; cin >> m >> n; try { cout << "before piding." << endl; if (n == 0) throw - 1; //抛出整型异常 else if (m == 0) throw - 1.0; //拋出 double 型异常 else cout << m / n << endl; cout << "after piding." << endl; } catch (double d) { cout << "catch (double)" << d << endl; } catch (...) { cout << "catch (...)" << endl; } cout << "finished" << endl; return 0;}
程序的运行结果如下:
9 0↙before piding.catch (...)finished
当 n 为 0 时,拋出的整型异常被catchy(...)捕获。
程序的运行结果也可能如下:
0 6↙before piding.catch (double) -1finished
当 m 为 0 时,拋出一个 double 类型的异常。虽然catch (double)和catch(...)都能匹配该异常,但是catch(double)是第一个能匹配的 catch 块,因此会执行它,而不会执行catch(...)块。
由于catch(...)能匹配任何类型的异常,它后面的 catch 块实际上就不起作用,因此不要将它写在其他 catch 块前面。
异常的再拋出
如果一个函数在执行过程中拋出的异常在本函数内就被 catch 块捕获并处理,那么该异常就不会拋给这个函数的调用者(也称为“上一层的函数”);如果异常在本函数中没有被处理,则它就会被拋给上一层的函数。
例如下面的程序:
#include <iostream>#include <string>using namespace std;class CException{public: string msg; CException(string s) : msg(s) {}};double Devide(double x, double y){ if (y == 0) throw CException("devided by zero"); cout << "in Devide" << endl; return x / y;}int CountTax(int salary){ try { if (salary < 0) throw - 1; cout << "counting tax" << endl; } catch (int) { cout << "salary < 0" << endl; } cout << "tax counted" << endl; return salary * 0.15;}int main(){ double f = 1.2; try { CountTax(-1); f = Devide(3, 0); cout << "end of try block" << endl; } catch (CException e) { cout << e.msg << endl; } cout << "f = " << f << endl; cout << "finished" << endl; return 0;}
程序的输出结果如下:
salary < 0tax counteddevided by zerof=1.2finished
CountTa 函数拋出异常后自行处理,这个异常就不会继续被拋给调用者,即 main 函数。因此在 main 函数的 try 块中,CountTax 之后的语句还能正常执行,即会执行f = Devide(3, 0);。
第 35 行,Devide 函数拋出了异常却不处理,该异常就会被拋给 Devide 函数的调用者,即 main 函数。拋出此异常后,Devide 函数立即结束,第 14 行不会被执行,函数也不会返回一个值,这从第 35 行 f 的值不会被修改可以看出。
Devide 函数中拋出的异常被 main 函数中类型匹配的 catch 块捕获。第 38 行中的 e 对象是用复制构造函数初始化的。
如果拋出的异常是派生类的对象,而 catch 块的异常类型是基类,那么这两者也能够匹配,因为派生类对象也是基类对象。
虽然函数也可以通过返回值或者传引用的参数通知调用者发生了异常,但采用这种方式的话,每次调用函数时都要判断是否发生了异常,这在函数被多处调用时比较麻烦。有了异常处理机制,可以将多处函数调用都写在一个 try 块中,任何一处调用发生异常都会被匹配的 catch 块捕获并处理,也就不需要每次调用后都判断是否发生了异常。
有时,虽然在函数中对异常进行了处理,但是还是希望能够通知调用者,以便让调用者知道发生了异常,从而可以作进一步的处理。在 catch 块中拋出异常可以满足这种需要。例如:
#include <iostream>#include <string>using namespace std;int CountTax(int salary){ try { if( salary < 0 ) throw string("zero salary"); cout << "counting tax" << endl; } catch (string s ) { cout << "CountTax error : " << s << endl; throw; //继续抛出捕获的异常 } cout << "tax counted" << endl; return salary * 0.15;}int main(){ double f = 1.2; try { CountTax(-1); cout << "end of try block" << endl; } catch(string s) { cout << s << endl; } cout << "finished" << endl; return 0;}
程序的输出结果如下:
CountTax error:zero salaryzero salaryfinished
第 14 行的throw;没有指明拋出什么样的异常,因此拋出的就是 catch 块捕获到的异常,即 string("zero salary")。这个异常会被 main 函数中的 catch 块捕获。
函数的异常声明列表
为了增强程序的可读性和可维护性,使程序员在使用一个函数时就能看出这个函数可能会拋出哪些异常,C++ 允许在函数声明和定义时,加上它所能拋出的异常的列表,具体写法如下:
void func() throw (int, double, A, B, C);
或
void func() throw (int, double, A, B, C){...}
上面的写法表明 func 可能拋出 int 型、double 型以及 A、B、C 三种类型的异常。异常声明列表可以在函数声明时写,也可以在函数定义时写。如果两处都写,则两处应一致。
如果异常声明列表如下编写:
void func() throw ();
则说明 func 函数不会拋出任何异常。
一个函数如果不交待能拋出哪些类型的异常,就可以拋出任何类型的异常。
函数如果拋出了其异常声明列表中没有的异常,在编译时不会引发错误,但在运行时, Dev C++ 编译出来的程序会出错;用 Visual Studio 2010 编译出来的程序则不会出错,异常声明列表不起实际作用。
C++标准异常类
C++ 标准库中有一些类代表异常,这些类都是从 exception 类派生而来的。常用的几个异常类如图 1 所示。
图1:常用的异常类
bad_typeid、bad_cast、bad_alloc、ios_base::failure、out_of_range 都是 exception 类的派生类。C++ 程序在碰到某些异常时,即使程序中没有写 throw 语句,也会自动拋出上述异常类的对象。这些异常类还都有名为 what 的成员函数,返回字符串形式的异常描述信息。使用这些异常类需要包含头文件 stdexcept。
下面分别介绍以上几个异常类。本节程序的输出以 Visual Studio 2010为准,Dev C++ 编译的程序输出有所不同。
1) bad_typeid
使用 typeid 运算符时,如果其操作数是一个多态类的指针,而该指针的值为 NULL,则会拋出此异常。
2) bad_cast
在用 dynamic_cast 进行从多态基类对象(或引用)到派生类的引用的强制类型转换时,如果转换是不安全的,则会拋出此异常。程序示例如下:
#include <iostream>#include <stdexcept>using namespace std;class Base{ virtual void func() {}};class Derived : public Base{public: void Print() {}};void PrintObj(Base & b){ try { Derived & rd = dynamic_cast <Derived &>(b); //此转换若不安全,会拋出 bad_cast 异常 rd.Print(); } catch (bad_cast & e) { cerr << e.what() << endl; }}int main(){ Base b; PrintObj(b); return 0;}
程序的输出结果如下:
Bad dynamic_cast!
在 PrintObj 函数中,通过 dynamic_cast 检测 b 是否引用的是一个 Derived 对象,如果是,就调用其 Print 成员函数;如果不是,就拋出异常,不会调用 Derived::Print。
3) bad_alloc
在用 new 运算符进行动态内存分配时,如果没有足够的内存,则会引发此异常。程序示例如下:
#include <iostream>#include <stdexcept>using namespace std;int main(){ try { char * p = new char[0x7fffffff]; //无法分配这么多空间,会抛出异常 } catch (bad_alloc & e) { cerr << e.what() << endl; } return 0;}
程序的输出结果如下:
bad allocationios_base::failure
在默认状态下,输入输出流对象不会拋出此异常。如果用流对象的 exceptions 成员函数设置了一些标志位,则在出现打开文件出错、读到输入流的文件尾等情况时会拋出此异常。此处不再赘述。
4) out_of_range
用 vector 或 string 的 at 成员函数根据下标访问元素时,如果下标越界,则会拋出此异常。例如:
#include <iostream>#include <stdexcept>#include <vector>#include <string>using namespace std;int main(){ vector<int> v(10); try { v.at(100) = 100; //拋出 out_of_range 异常 } catch (out_of_range & e) { cerr << e.what() << endl; } string s = "hello"; try { char c = s.at(100); //拋出 out_of_range 异常 } catch (out_of_range & e) { cerr << e.what() << endl; } return 0;}
程序的输出结果如下:
invalid vector <T> subscriptinvalid string position
如果将v.at(100)换成v[100],将s.at(100)换成s[100],程序就不会引发异常(但可能导致程序崩溃)。因为 at 成员函数会检测下标越界并拋出异常,而 operator[] 则不会。operator [] 相比 at 的好处就是不用判断下标是否越界,因此执行速度更快。
以上就是C++中怎么使用trythrowcatch进行异常处理,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。