try catch


在C++中,trycatch是异常处理机制的关键字。异常处理是一种处理程序中特殊情况(即异常)的机制,这些情况通常在程序正常运行时不会发生,但一旦发生就需要特殊处理。


基本结构

异常处理的基本结构如下:

try {
    // 尝试执行的代码块
    // 这里可能会抛出异常
}
catch (异常类型1 e) {
    // 处理异常类型1的代码
}
catch (异常类型2 e) {
    // 处理异常类型2的代码
}
// ... 可以有多个catch块来处理不同类型的异常
catch (...) {
    // 处理所有未被前面的catch块捕获的异常
}
  1. try块:在try块中,你放置可能会抛出异常的代码。
  2. catch块catch块用于捕获并处理try块中抛出的异常。你可以为不同类型的异常提供不同的catch块。如果try块中的代码抛出了一个异常,那么与该异常类型匹配的catch块将被执行。
  3. 异常类型:在catch关键字后面的括号中,你指定要捕获的异常的类型。这可以是任何内置类型、用户定义类型或标准库中的异常类型(如std::exception)。
  4. 异常变量(可选):在异常类型后面,你可以指定一个变量名来存储捕获到的异常对象。这样,你就可以在catch块内部访问该异常对象的属性和方法。
  5. 捕获所有异常:使用三个点(...)作为异常类型,可以捕获所有类型的异常。这通常用作最后的catch块,以确保所有未被特定处理的异常都能得到某种形式的处理。

抛出异常

在C++中,使用throw关键字来抛出异常。例如:

throw std::runtime_error("发生了一个运行时错误");

这里,std::runtime_error是一个标准库中的异常类型,但你也可以抛出任何类型的对象作为异常。

异常处理的流程

  1. 当在try块中遇到throw语句时,程序的控制流将立即离开当前的执行路径,并查找与抛出的异常类型相匹配的catch块。
  2. 如果找到了匹配的catch块,程序将执行该块中的代码,然后继续执行紧跟在该catch块之后的代码(如果有的话)。
  3. 如果没有找到匹配的catch块,并且存在一个捕获所有异常的catch(...)块,那么将执行该块。
  4. 如果没有找到任何匹配的catch块,并且也没有捕获所有异常的块,那么程序将调用std::terminate函数并异常终止。

注意事项

  • 异常处理机制不是用来处理常规错误的。它应该用于处理那些如果不加以处理就可能导致程序崩溃或产生不可预测行为的异常情况。
  • 过度使用异常处理可能会使代码难以理解和维护。因此,应谨慎使用异常处理,并遵循良好的编程实践。

常见的标准异常类型

在C++中,异常类型非常多样,包括标准库定义的异常和用户自定义的异常。标准库中的异常主要定义在<stdexcept><exception>头文件中。以下是一些常见的标准异常类型:

  1. std::exception:所有标准异常类的基类。
  2. std::bad_alloc:在动态内存分配失败时抛出。
  3. std::bad_cast:在动态类型转换失败时抛出。
  4. std::bad_exception:当异常处理函数抛出异常时抛出(不过请注意,这个异常类型在实际应用中较少使用,因为它与C++的异常处理机制的一些早期设计有关)。
  5. std::bad_typeid:在typeid操作符应用于null指针或空引用时抛出。
  6. std::logic_error及其派生类:表示程序逻辑错误,如无效的参数值。这些异常通常在程序员的控制范围内,应该在开发阶段就被捕获并处理。
  7. std::runtime_error及其派生类:表示运行时错误,如文件不存在或无法打开。这些异常通常是由外部因素引起的,程序员可能无法完全预防。

用户还可以定义自己的异常类,这些类通常从std::exception或其派生类继承而来。


示例

当然,下面是一个简单的C++程序,展示了如何使用trycatch块来处理异常。

#include <iostream>
#include <stdexcept> // 包含标准异常类的头文件

int divide(int a, int b) {
    if (b == 0) {
        throw std::invalid_argument("Divisor cannot be zero!"); // 抛出异常
    }
    return a / b;
}

int main() {
    int dividend = 10;
    int divisor = 0; // 设置为0来触发异常
    try {
        int result = divide(dividend, divisor);
        std::cout << "The result of the division is: " << result << std::endl;
    } catch (const std::invalid_argument& e) {
        // 处理特定类型的异常
        std::cerr << "Caught an invalid_argument exception: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        // 处理其他所有标准异常
        std::cerr << "Caught an exception of an unexpected type: " << e.what() << std::endl;
    } catch (...) {
        // 捕获所有未被前面的catch块捕获的异常
        std::cerr << "Unknown exception caught" << std::endl;
    }

    std::cout << "Program execution continues..." << std::endl;
    return 0;
}

在这个例子中,我们定义了一个divide函数,它接受两个整数作为参数,并返回它们的商。如果除数为零,函数会抛出一个std::invalid_argument异常。

main函数中,我们尝试调用divide函数,并将其结果打印到控制台。我们使用try块来包围可能会抛出异常的代码,并使用多个catch块来处理不同类型的异常。

  1. 第一个catch块专门处理std::invalid_argument异常,这是我们在divide函数中抛出的特定类型的异常。
  2. 第二个catch块处理所有从std::exception派生的其他标准异常。由于std::invalid_argument是从std::exception派生的,因此这个catch块实际上不会捕获到std::invalid_argument异常,因为第一个catch块已经捕获了它。但是,如果有其他类型的标准异常被抛出,这个catch块将会捕获它们。
  3. 最后一个catch块使用三个点(...)作为异常类型,这意味着它将捕获所有未被前面的catch块捕获的异常。这是一个通用的异常处理程序,通常用于确保程序在未知异常发生时不会崩溃。

注意,在这个例子中,由于除数为零,程序将执行到throw std::invalid_argument("Divisor cannot be zero!");语句,并立即跳转到与之匹配的catch块。在这种情况下,将打印出“Caught an invalid_argument exception: Divisor cannot be zero!”消息,并且程序将继续执行,打印出“Program execution continues...”消息。

posted @ 2024-03-21 20:49  guanyubo  阅读(88)  评论(0)    收藏  举报