【C++】:异常机制语法

137 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

1️⃣前言

今天的笔记内容是:C++异常处理的语法


2️⃣异常语法

🌱关键字

C++的异常处理有三个关键字:

  • throw抛出异常(通过throw关键字来抛出异常)
  • try激活异常(在try语句块中编写可能发生异常的代码)
  • catch捕获异常(用catch关键字来捕获异常并处理)

举个简单的例子:

#include <iostream>
using namespace std;
int func(int a, int b) {
    if( b == 0 ){
        throw 1;
    }
    return a / b;
}
int main()
{
    int a = 8;
    int b = 4;
    int c = 0;
    // 在try语句块中编写可能发现异常的代码
    try {
        int result1 = func(a, b); // 无异常 
        cout<< a << "/" << b << "=" << result1 << endl;
        int result2 = func(a, c); // 有异常(c的值为0) 
        cout<< a << "/" << c << "=" << result2 << endl;
    }
    // 捕获异常并做出处理 
    catch(int) {
        cout<<"出现异常!除数不能为0"; 
    } 
    return 0;
}

运行结果如下:

🌳小结

  1. 如果有异常发生,可以用throw操作,创建一个异常对象并抛出;
  2. 将可能发生异常的代码放在try语句块中;
  3. 如果try语句在执行时没有引起异常,则catch代码块就不会执行;
  4. 如果try语句块中发生异常,则catch子句则会根据出现的先后顺序逐一检查;
  5. 匹配到的catch语句(即捕获了相应异常)则会处理异常(或者也可以继续抛出异常);
  6. 如果没有匹配的catch语句块,程序就会终止;
  7. C++的异常处理机制使得异常的发生和异常的处理不必拘泥于一个函数中,这样底层的函数可以着重解决相应的问题而不必过多考虑异常的处理;
  8. 让调用者可以在适当的位置设置对不同类型异常的处理。

3️⃣关键字详解

前面讲到,C++异常处理的流程为:

抛出(throw)--> 检测(try) --> 捕获(catch)

所以,异常必须显式地抛出,才能被检测和捕获到。下面重点看看各个关键字的细节。

📗throw

用法

throw exceptionData;

exceptionData就是异常数据,可以是任意的数据类型(intdoublechar*等),由我们自己决定。

因此,throw的作用就是在函数体中抛出异常。

扩展

  • 另外的,throw还可以用作异常规范(或者叫异常列表、异常指示符)
  • throw可以在函数头和函数体之间声明,指出当前函数能够抛出的异常类型,这就是异常规范。

1)例1

int func1 (char x) throw (int);
  • 函数func1并且只能抛出int类型的异常;
  • 如果抛出其他类型的异常,try将无法捕获,程序只能终结。

2)例2

int func2 (char y) throw (int, double);
  • 函数func2可以抛出多种异常,用逗号隔开

3)例3

int func3 (char z) throw ();
  • 函数func3不会抛出任何异常,throw( )中什么也不用写;
  • 即使抛出了,try 也检测不到。

需要注意的是,异常规范的初衷是为了让程序员看到函数的定义或声明后,能够立马就知道该函数会抛出什么类型的异常,这样程序员就可以使用try-catch来捕获了。如果没有异常规范,程序员必须阅读函数源码才能知道函数会抛出什么异常。但由于异常规范的初衷较难实现,后来的 C++11 也将其抛弃了,因此我们不建议使用异常规范。

📙try-catch

用法

trycatch关键字是一起使用的。用法相当于让try语句块去检测代码运行时有没有异常,一旦有异常抛出就会被catch所捕获。换句话说,如果try语句块没有检测到异常(没有异常抛出),那么就不会执行catch中的语句。

语法如下:

1)例1

try {
    // 将可能发生异常的代码放置在try语句块中
}
catch(异常类型 变量名) {
    // 捕获相应异常并处理
}

2)例2

try {
    // 将可能发生异常的代码放置在try语句块中
}
catch(...) {
    // 加省略号表示可以处理try抛出的任何类型的异常
}

3)例3

//可以有多级catch语句
try {
    // 将可能发生异常的代码放置在try语句块中
}
catch(异常类型 变量名) {
    // 捕获相应异常并处理
}
catch(异常类型 变量名) {
    // 捕获相应异常并处理
}
catch(...) {
    // 加省略号表示可以处理try抛出的任何类型的异常
}

小结

  1. 只有当异常被明确抛出时(明确地告诉程序发生了什么错误),try语句才能检测到。也就是说,如果发生错误,异常又没有抛出,则try语句是检测不到的。
  2. try语句块中,一旦异常被抛出,则会立刻被try检测到,并且程序执行发生跳转,从异常抛出点跳转到catch处,将异常交给catch语句块处理。也就是说,位于异常抛出点之后的语句不会再执行。
  3. 执行完catch块的代码后,程序会继续执行catch块后面的代码,即恢复正常的执行流程。

4️⃣异常发生的位置

无论在当前try块中直接抛出异常,还是在try中调用的某个函数发生了异常,都可以被try检测到。

try中直接抛出异常

#include <iostream>
using namespace std;
int main()
{
	try {
        throw 1; //抛出异常 
        cout<<"程序运行中..."<<endl;
    }
	catch(int num) {
        cout << num << endl;
    }
	return 0;
}

运行结果为:

1

try中调用的函数抛出异常

#include <iostream>
using namespace std;
void func() {
	throw 1; // 抛出异常 
} 
int main()
{
	try {
        func();
        cout<<"程序运行中..."<<endl;
    }
	catch(int num) {
        cout << num << endl;
    }
	return 0;
}

运行结果为:

1

5️⃣写在最后

好了,本篇笔记就到写这,欢迎大家到评论区一起讨论!