C++中的异常处理机制(新手必看)
在程序运行过程中,外界环境的异常情况可能引发程序执行出错,导致错误的结果。
例如,程序试图打开一个未被占用的文件进行写入,在正常情况下可以成功执行。但如果文件已被占用,程序就会执行失败。这种不正常的情况就是所谓的程序执行过程中的异常。常见的异常情况还包括数组下标越界、系统内存不足、以 0 作为除数等。
一旦程序执行过程中发生异常,可能会引发计算失效、程序运行时无故停止,甚至程序崩溃等严重后果。这些异常情况,虽然像现实世界中的地震一样无法避免且难以预料,但我们可以在开发程序时进行必要的异常处理,以降低用户的损失。
C++ 专门提供了异常处理机制,这是一种在运行时对出现的异常情况进行捕获并处理的方法。
C++ 中的异常处理使用 try 关键字来尝试执行可能会出现异常的代码段。当代码段执行过程中发生异常时,系统会抛出相应类型的异常。随后,catch 关键字会对异常进行捕获,并由相应类型的异常处理分支对其进行恰当的处理,如结束正在执行的操作、清理不再需要的资源等,从而避免更大错误的发生,尽可能地挽回用户的损失。
在 C++ 中,异常处理的基本语法格式如下:
用 throw 关键字抛出一个异常的语法格式如下:
例如:
当 try 语句块中抛出某个类型的异常后,该异常会被紧跟其后的相应类型的 catch 语句捕获并对其进行处理。catch 语句可以带有一个形式参数,它的类型就是 catch 语句要捕获的异常类型,也就是说,异常被某个 catch 语句捕获的条件是该异常的类型与 catch 语句的异常类型相匹配。
当 throw 关键字抛出的异常被某个 catch 语句捕获时,throw 关键字后的异常表达式会被当成实际参数传递给 catch 语句中的形式参数,进而 catch 语句可以根据这个参数提供的信息对异常进行具体的处理。
当需要捕获多种类型的异常时,可以将多个 catch 语句并列。如果省略 catch 关键字后面的形式参数而使用…代替,则表示 catch 语句会捕获所有类型的异常。
当 try 语句块中抛出异常并被某个 catch 分支捕获后,异常处理会把程序的执行从异常发生的地点转移到捕获这个异常的 catch 分支语句,随后对异常进行具体的处理。
例如:
当异常发生时,也就是用户输入的除数为 0 时,Divide() 函数会抛出一个描述了错误信息的字符串类型的异常。在 try 语句块之后的 catch 语句会捕获这个字符串类型的异常并进行处理。有了异常处理,即使用户不小心输入了除数为 0,程序也不会直接给出一个错误结果,而是会提示用户错误的发生及其原因,帮助用户修复问题。这样可以提供良好的用户体验,避免用户对程序产生不满。
当然,这里对异常的处理只是将这个错误信息输出报告给用户而已。在实际应用中,异常处理往往更加复杂,包括资源的清理回收、记录错误日志等。
异常处理的三个步骤如下图所示:

图 1 异常处理的三个步骤
总之,使用异常处理这一方法,可以在错误发生后尽量补救错误所造成的损失,最大程度地减少对用户的影响,同时增强程序的健壮性,也让程序员少挨一点骂。
例如,程序试图打开一个未被占用的文件进行写入,在正常情况下可以成功执行。但如果文件已被占用,程序就会执行失败。这种不正常的情况就是所谓的程序执行过程中的异常。常见的异常情况还包括数组下标越界、系统内存不足、以 0 作为除数等。
一旦程序执行过程中发生异常,可能会引发计算失效、程序运行时无故停止,甚至程序崩溃等严重后果。这些异常情况,虽然像现实世界中的地震一样无法避免且难以预料,但我们可以在开发程序时进行必要的异常处理,以降低用户的损失。
C++ 专门提供了异常处理机制,这是一种在运行时对出现的异常情况进行捕获并处理的方法。
C++ 中的异常处理使用 try 关键字来尝试执行可能会出现异常的代码段。当代码段执行过程中发生异常时,系统会抛出相应类型的异常。随后,catch 关键字会对异常进行捕获,并由相应类型的异常处理分支对其进行恰当的处理,如结束正在执行的操作、清理不再需要的资源等,从而避免更大错误的发生,尽可能地挽回用户的损失。
在 C++ 中,异常处理的基本语法格式如下:
// 用 try 开始异常处理语句 try { // 可能发生异常的语句 } catch(异常类型 [形参名]) // 捕获特定类型的异常 { // 对此类型的异常进行处理 } catch(异常类型 [形参名]) // 捕获特定类型的异常 { // 对此类型的异常进行处理 } // 可以有多个 catch 语句并列,捕获不同类型的异常 catch(...) // 如果省略具体的异常类型用...表示,则表示捕获所有类型的异常 { // 对所有类型的异常进行处理 }当我们认为某段代码可能会出现异常情况并需要处理时,可以把这段代码放入 try 关键字后面的代码块中。在程序执行 try 语句块中的语句(包括其中调用的函数)时,如果遇到异常情况,可以使用 throw 关键字抛出一个相应类型的异常,表示某种异常情况的发生,需要后面的 catch 语句捕获并对其进行处理。
用 throw 关键字抛出一个异常的语法格式如下:
throw 异常表达式;其中,异常表达式就是要抛出的异常,它可以是表示异常类型的错误代码,或者是含有异常相关信息的某个对象,总之,它的意义是为异常处理提供相应的辅助信息。
例如:
// 除法函数 double Divide(int a, int b) { if(0 == b) throw "不能使用 0 作为除数"; return (double)a/b; }在这个除法函数中,当检测到除数为 0 时,使用 throw 关键字抛出一个异常,以提前结束这个函数,跳过后面的除法运算,避免错误的发生。这里 throw 关键字抛出的异常是一个字符串,它描述了异常发生的原因,便于程序员对其进行处理。当然,还可以抛出专门的异常对象,只要该异常对象的类型是事先定义好的,并能够给后面的异常处理提供足够的信息即可。
当 try 语句块中抛出某个类型的异常后,该异常会被紧跟其后的相应类型的 catch 语句捕获并对其进行处理。catch 语句可以带有一个形式参数,它的类型就是 catch 语句要捕获的异常类型,也就是说,异常被某个 catch 语句捕获的条件是该异常的类型与 catch 语句的异常类型相匹配。
当 throw 关键字抛出的异常被某个 catch 语句捕获时,throw 关键字后的异常表达式会被当成实际参数传递给 catch 语句中的形式参数,进而 catch 语句可以根据这个参数提供的信息对异常进行具体的处理。
当需要捕获多种类型的异常时,可以将多个 catch 语句并列。如果省略 catch 关键字后面的形式参数而使用…代替,则表示 catch 语句会捕获所有类型的异常。
当 try 语句块中抛出异常并被某个 catch 分支捕获后,异常处理会把程序的执行从异常发生的地点转移到捕获这个异常的 catch 分支语句,随后对异常进行具体的处理。
例如:
// 开始异常处理语句 try { cout<<"请输入被除数与除数:"<<endl; int a,b; // 被除数与除数 cin>>a>>b; // 接收用户输入 // 进行除法运算,当 b 为 0 时会抛出异常 double fRes = Divide(a, b); cout<<a<<"/"<<b<<" = "<<fRes<<endl; // 输出结果 } // 捕获 try 语句块中所抛出的字符串类型异常 catch( char* pMsg ) { // 对异常进行处理 // 这里只输出错误信息 cout<<"程序运行发生异常:"<<pMsg<<endl; }在这段代码中,我们使用 Divide() 函数来完成一个除法运算。但这个函数在除数为 0 的情况下会发生异常。为了确保程序的正确性和用户的友好使用体验,我们将除法运算放到 try 语句块中执行。
当异常发生时,也就是用户输入的除数为 0 时,Divide() 函数会抛出一个描述了错误信息的字符串类型的异常。在 try 语句块之后的 catch 语句会捕获这个字符串类型的异常并进行处理。有了异常处理,即使用户不小心输入了除数为 0,程序也不会直接给出一个错误结果,而是会提示用户错误的发生及其原因,帮助用户修复问题。这样可以提供良好的用户体验,避免用户对程序产生不满。
当然,这里对异常的处理只是将这个错误信息输出报告给用户而已。在实际应用中,异常处理往往更加复杂,包括资源的清理回收、记录错误日志等。
异常处理的三个步骤如下图所示:

图 1 异常处理的三个步骤
总之,使用异常处理这一方法,可以在错误发生后尽量补救错误所造成的损失,最大程度地减少对用户的影响,同时增强程序的健壮性,也让程序员少挨一点骂。