c和cpp的异常处理

### 课堂讨论

**老师**:今天我们来深入探讨一下C++的异常处理机制。想象一下,我们正在玩一场探险游戏。你会遇到一些意外情况,比如掉进陷阱。这就像我们的程序在运行中遇到错误。我们该怎么处理呢?🤔

**学生**:嗯,是不是用`try`、`catch`和`throw`?

**老师**:没错!这是我们处理这些“陷阱”的三件法宝。

没错,你总结得很好!我们可以通过这三个关键步骤来理解异常处理:

1. **try块**:这是你放置可能会出错的代码的地方。就像在探险中,你准备尝试穿越一片未知的森林。你知道可能会遇到一些危险,但你还是要前进。

2. **throw表达式**:当在`try`块中发现了问题(比如你遇到了一个大陷阱),你可以用`throw`来“抛出”一个异常,就像发出求救信号,告诉程序这里出了问题。

3. **catch块**:这是用来捕获和处理那些抛出的异常的地方。就像接收到求救信号后,救援队会过来帮你解决问题。你可以针对不同类型的异常写不同的`catch`块,确保每种情况都有相应的解决方案。

首先,`try`块就像踏入未知的森林,你知道可能会有危险,但还是要勇敢前进。你能想到什么场景会用到`try`吗?

**学生**:比如读取文件时,文件可能不存在?

**老师**:正是如此!我们来看一个例子:

```cpp
#include <iostream>
#include <fstream>
#include <stdexcept>

int main() {
    try {
        std::ifstream file("data.txt");
        if (!file) {
            throw std::runtime_error("文件未找到!📁❌");
        }
        // 处理文件...
    } catch (const std::runtime_error& e) {
        std::cout << "捕获到异常: " << e.what() << '\n';
    }
    return 0;
}
```

**老师**:在这个例子中,我们尝试打开一个文件。

`std::ofstream` 和 `std::ifstream` 分别是 "output file stream" 和 "input file stream" 的缩写。

- **`std::ofstream`**:
  - **Output File Stream**: 用于输出(写入)文件的流。

- **`std::ifstream`**:
  - **Input File Stream**: 用于输入(读取)文件的流。

这些流类提供了与文件进行交互的简单接口,允许程序读取和写入文件数据。

当然,这段代码的每一句都可以通过其语法结构来理解:

1. **`#include <iostream>`**
   - **语法结构**:预处理指令
   - **作用**:包含标准输入输出流库,使得程序可以使用`std::cout`和`std::cin`进行控制台输入输出。

2. **`#include <fstream>`**
   - **语法结构**:预处理指令
   - **作用**:包含文件流库,允许程序使用文件输入输出流,如`std::ifstream`和`std::ofstream`,用于文件操作。

3. **`#include <stdexcept>`**
   - **语法结构**:预处理指令
   - **作用**:包含标准异常库,使得程序可以使用标准异常类如`std::runtime_error`,用于抛出和处理异常。

4. **`int main() {`**
   - **语法结构**:函数定义
   - **作用**:定义程序的入口函数`main`,返回类型为`int`,表示程序的执行状态。大括号`{}`包围了函数的主体。

5. **`try {`**
   - **语法结构**:异常处理结构的起始
   - **作用**:标记一段可能抛出异常的代码块。程序在此块中执行代码,并在遇到异常时将控制转移到相应的`catch`块。

6. **`std::ifstream file("data.txt");`**
   - **语法结构**:对象创建和初始化
   - **作用**:创建一个输入文件流对象`file`,并尝试打开名为`data.txt`的文件。`std::ifstream`是一个来自`<fstream>`库的类,用于读取文件。

7. **`if (!file) {`**
   - **语法结构**:条件语句
   - **作用**:检查文件流对象`file`的状态。如果文件未成功打开(即`file`为假),则进入条件内的代码块。

8. **`throw std::runtime_error("文件未找到!📁❌");`**
   - **异常抛出
   - **作用**:创建并抛出一个`std::runtime_error`类型的异常,包含错误信息字符串"文件未找到!📁❌"。这将把程序控制转移到`catch`块。

9. **`// 处理文件...`**
   - **语法结构**:注释
   - **作用**:说明性的文本,供程序员阅读。标识出在这里可以放置处理文件的代码。

10. **`} catch (const std::runtime_error& e) {`**
    - **语法结构**:异常捕获
    - **作用**:捕获从`try`块中抛出的`std::runtime_error`类型异常。`e`是捕获的异常对象的引用,允许访问异常信息。

在 `catch (const std::runtime_error& e)` 这一部分中,每个部分的意思如下:

- **`const`**: 指定`e`是一个常量引用。这意味着在`catch`块中不能修改`e`所引用的异常对象。这是一个常见的做法,因为通常不需要修改异常对象,只需要读取其中的信息。

- **`std::runtime_error`**: 这是一个标准库中定义的异常类,继承自`std::exception`。它用于表示程序运行时的错误。`std::runtime_error`通常包含一个错误消息,可以通过调用`what()`方法获取。

- **`&`**: 表示引用。这里是捕获异常的引用,使用引用可以避免对象的拷贝,同时也确保使用的是抛出时的异常对象。

- **`e`**: 这是异常对象的名称。在`catch`块中,可以通过这个名字访问捕获的异常对象,通常用于获取错误信息或进行其他异常处理。

在 `catch (const std::runtime_error& e)` 中,`&` 用于声明 `e` 是对抛出的异常对象的引用。这避免了在捕获异常时对异常对象的拷贝。具体来说:

### 避免的拷贝

- **对象拷贝**: 当异常被抛出时,通常会创建一个异常对象。通过使用引用来捕获这个对象,可以避免在 `catch` 块中再次拷贝这个对象,从而提高性能并减少内存使用。这在处理大型对象或者复杂异常类型时尤为重要。

### `&` 的其他意思

1. **取地址符**:
   - 用于获取变量的内存地址。
   - 例如:`int x = 10; int* ptr = &x;` 这里 `&x` 获取变量 `x` 的地址并赋值给指针 `ptr`。

2. **按位与运算符**:
   - 用于对整数执行按位与运算。
   - 例如:`int a = 5, b = 3; int c = a & b;` 这里 `a & b` 计算结果为 `1`,因为二进制的按位与运算。

3. **引用符**:
   - 用于声明一个变量是引用类型。
   - 例如:`int x = 5; int& ref = x;` 这里 `ref` 是 `x` 的引用,允许通过 `ref` 直接访问和修改 `x`。

4. **右值引用(C++11及之后)**:
   - 用作右值引用符号(`&&`),用于移动语义。
   - 例如:`void foo(int&& x);` 这里 `x` 是一个右值引用,用于实现移动语义以优化性能。

这些不同的用法在语法上和功能上是不同的,具体的含义取决于 `&` 所处的上下文。

总结一下,`catch (const std::runtime_error& e)`的作用是捕获一个类型为`std::runtime_error`的异常,并通过引用`e`来访问异常对象的详细信息,如错误消息。

11. **`std::cout << "捕获到异常: " << e.what() << '\n';`**
    - **语法结构**:输出语句
    - **作用**:使用标准输出流`std::cout`输出捕获到的异常信息。`e.what()`调用异常对象的方法,返回一个指向异常描述的字符串。`'\n'`用于换行。

12. **`return 0;`**
    - **语法结构**:返回语句
    - **作用**:从`main`函数返回整数值`0`,表示程序正常结束。C++标准规定,返回`0`通常表示成功执行。

13. **`}`**
    - **语法结构**:块结束符
    - **作用**:关闭先前打开的`try-catch`块以及`main`函数的定义。

如果文件不存在,我们就`throw`一个异常,这就像发出了求救信号。然后呢,`catch`块就好比救援队,他们接收到信号后会过来帮我们解决问题。😀

**学生**:明白了!那`throw`表达式具体是怎么工作的?

**老师**:`throw`就是在告诉程序“这里有问题”,它会跳出`try`块,把控制权交给相应的`catch`块。再来看一个例子,这次是处理除零错误:

```cpp
#include <iostream>
#include <stdexcept>

double divide(double a, double b) {
    if (b == 0) {
        throw std::invalid_argument("除数不能为零!❌");
    }
    return a / b;
}

int main() {
    try {
        double result = divide(10, 0);
        std::cout << "结果是: " << result << '\n';
    } catch (const std::invalid_argument& e) {
        std::cout << "捕获到异常: " << e.what() << '\n';
    }
    return 0;
}
```

**学生**:哦,我看到当`b`是零的时候,`throw`就启动了,然后`catch`块捕获并处理了这个问题。

**老师**:没错!`catch`块可以处理特定类型的异常,确保程序不会崩溃。这里用到的是`std::invalid_argument`,专门处理无效参数的问题。😊

**学生**:这让我想到,如果网络请求失败,也可以用这种方式处理?

**老师**:完全正确!假设你在获取一个远程服务器的数据,如果连接失败,你可以抛出一个异常来处理。看这个例子:

```cpp
#include <iostream>
#include <stdexcept>

void fetchDataFromServer() {
    // 模拟网络请求失败
    bool networkError = true;
    if (networkError) {
        throw std::runtime_error("网络请求失败!🌐❌");
    }
    // 处理请求...
}

int main() {
    try {
        fetchDataFromServer();
    } catch (const std::runtime_error& e) {
        std::cout << "捕获到异常: " << e.what() << '\n';
    }
    return 0;
}
```

**学生**:我明白了,`try`、`catch`、`throw`的组合就像一套完整的异常处理系统,帮助我们优雅地处理程序中的意外情况。

**老师**:你总结得很好!这三者的协作使得我们的程序更健壮,也更容易维护。有什么不清楚的地方吗?

**学生**:没有了,这次我完全理解了!谢谢老师!🙌

### 思维导图总结

- **异常处理机制**
  - **try块**:执行可能出错的操作
  - **throw表达式**:抛出异常,表明错误发生
  - **catch块**:捕获并处理异常

### 思考题

1. **问题**:编写一个C++程序,模拟一个购物车系统,尝试从空购物车中移除商品,并使用异常处理来处理这个错误。

   **答案**:
   ```cpp
   #include <iostream>
   #include <stdexcept>
   #include <vector>

   void removeItem(std::vector<int>& cart) {
       if (cart.empty()) {
           throw std::out_of_range("购物车是空的,无法移除商品!🛒❌");
       }
       cart.pop_back();
   }

   int main() {
       try {
           std::vector<int> cart;
           removeItem(cart);
       } catch (const std::out_of_range& e) {
           std::cout << "捕获到异常: " << e.what() << '\n';
       }
       return 0;
   }
   ```

2. **问题**:解释为什么在处理异常时,`catch`块中的异常类型很重要。

   **答案**:`catch`块中的异常类型用于匹配抛出的异常类型。如果异常类型不匹配,`catch`块不会捕获该异常。这允许程序设计者针对不同的错误类型提供不同的解决方案,提高了程序的灵活性和健壮性。根据不同的异常类型提供针对性的处理逻辑,可以更有效地解决问题并提供有用的反馈。

总结:

在C++中,`try-catch`语句用于异常处理。这个机制允许程序在运行时捕获错误,并通过适当的异常处理代码进行处理。以下是一个简单的示例,展示了如何使用`try-catch`来处理异常:

 

```cpp
#include <iostream> // 包含输入输出流库,用于输入输出操作
#include <stdexcept> // 包含标准异常库,用于处理异常

int main() { // 主函数,程序执行的入口
    try { // 尝试执行以下代码块
        std::cout << "Trying to do something risky...\n"; // 输出信息到控制台
        throw std::runtime_error("An error occurred!"); // 抛出一个运行时错误异常
    } catch (const std::runtime_error& e) { // 捕获运行时错误异常
        std::cout << "Caught a runtime error: " << e.what() << '\n'; // 输出异常信息
    } catch (...) { // 捕获任何其他异常
        std::cout << "Caught an unknown exception\n"; // 输出捕获未知异常的信息
    }
    
    std::cout << "Continuing execution\n"; // 输出继续执行的信息
    return 0; // 返回0,表示程序成功结束
}
```

#### 先修知识

- **C++语法基础**:了解如何定义和使用函数、变量、控制结构等。
- **异常处理**:理解`try-catch`块的用途,用于捕获和处理程序执行中的异常。
- **标准库**:了解C++标准库中的`iostream`和`stdexcept`,前者用于输入输出操作,后者用于异常处理。

### 3. 分类举例说明这个代码用来做什么?

这个代码演示了异常处理的基本用法。通常用于以下场景:

- **错误捕获**:当程序出现不可预见的错误时,通过异常机制捕获错误并进行适当处理,而不是终止程序。
- **资源管理**:在操作文件、网络连接等资源时,异常处理可确保资源被正确释放。
- **输入验证**:在处理用户输入时,使用异常处理机制来捕获和处理无效输入。

### 4. 设计一道类似作用的代码题

#### 题目

编写一个C++程序,尝试打开一个文件读取内容,如果文件不存在或无法打开,抛出异常并捕获,输出相应的错误信息。

#### 设计思路

1. **包含必要库**:需要包含用于文件操作的库。
2. **定义文件名**:使用硬编码的文件名,便于测试。
3. **尝试打开文件**:使用`std::ifstream`尝试打开文件。
4. **抛出异常**:如果文件打开失败,抛出异常。
5. **捕获异常**:使用`try-catch`结构捕获并处理异常。
6. **输出结果**:根据情况输出成功或错误信息。

#### 带逐行注释的代码

```cpp
#include <iostream> // 包含输入输出流库
#include <fstream> // 包含文件流库,用于文件操作
#include <stdexcept> // 包含标准异常库,用于处理异常

int main() {
    const char* filename = "example.txt"; // 定义要打开的文件名
    
    try { // 尝试执行以下代码块
        std::ifstream file(filename); // 创建文件输入流对象
        if (!file) { // 判断文件是否打开成功
            throw std::runtime_error("File cannot be opened"); // 抛出异常,文件无法打开
        }
        std::cout << "File opened successfully.\n"; // 文件打开成功时输出信息
        // 可以在此处添加读取文件内容的代码
        
    } catch (const std::runtime_error& e) { // 捕获运行时错误异常
        std::cout << "Error: " << e.what() << '\n'; // 输出异常信息
    } catch (...) { // 捕获任何其他异常
        std::cout << "An unknown error occurred\n"; // 输出未知错误信息
    }
    
    std::cout << "Program continues...\n"; // 输出程序继续执行的信息
    return 0; // 返回0,表示程序成功结束
}
```

这个程序演示了如何使用异常处理来处理文件操作中的错误情形。当文件无法打开时,程序不会崩溃,而是通过异常机制输出错误信息,并继续执行后续代码。

 

在C语言中没有直接的`try`语法。`try-catch`结构是用于异常处理的,并且通常是在C++或其他高级语言中使用。在C语言中,异常处理通常通过返回错误代码和使用`setjmp`和`longjmp`函数来实现。以下是一个简单的示例,展示了如何使用`setjmp`和`longjmp`进行错误处理:

```c
#include <stdio.h>
#include <setjmp.h>

jmp_buf buffer;

void errorFunction() {
    printf("An error occurred, jumping back!\n");
    longjmp(buffer, 1);  // Jump back to the saved point
}

int main() {
    if (setjmp(buffer) == 0) {
        printf("Starting try block\n");
        errorFunction();  // Simulate an error
        printf("This will not be printed\n");
    } else {
        printf("Caught an error\n");
    }
    
    printf("Continuing execution\n");
    return 0;
}
```

在这个例子中,`setjmp`用于保存当前的执行环境,如果调用`longjmp`,则程序会返回到`setjmp`的调用点,并从那里继续执行。这种方法可以模拟类似`try-catch`的行为,但需要手动管理。

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/469344.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

超详细:三大范式和反范式设计详解

目录 1、三大范式 第一范式&#xff1a; 列不可再分 。 第二范式&#xff1a; 行可以唯一区分 第三范式&#xff1a;确保数据的完整性、减少数据冗余和避免更新异常。 反方式模式 实验数据&#xff1a;模拟两张百万量级的数据表 反范式优化实验对比 反范式存在的问题 &am…

Python 实现阿里滑块全攻略

阿里划块技术为开发者提供了高精度的视觉分割能力&#xff0c;而 Python 作为一种简洁高效的编程语言&#xff0c;可以轻松调用阿里划块接口&#xff0c;实现各种场景下的图像分割需求。 Python 调用阿里云分割抠图 - 商品分割接口的步骤如下&#xff1a;首先&#xff0c;开通…

尤雨溪都点赞的测试工具,你还不用?

尤雨溪都点赞的测试工具&#xff0c;你还不用&#xff1f; 想要一个跨浏览器兼容、无闪退的测试体验&#xff1f;Playwright来了&#xff01;它不仅支持主流浏览器自动化&#xff0c;还能轻松跨平台&#xff0c;让你轻松构建可靠的端到端测试。本文带你了解Playwright的功能亮点…

「IDE」VS2022插件 Visual Assist X 番茄助手介绍说明

✨博客主页何曾参静谧的博客📌文章专栏「IDE」集成开发环境📚全部专栏「Win」Windows程序设计「IDE」集成开发环境「UG/NX」BlockUI集合「C/C++」C/C++程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「UG/NX」NX定制开发「Py」Py

qt QFrame详解

1、概述 QFrame是Qt框架中用于提供框架或边框的控件&#xff0c;主要用于在图形用户界面&#xff08;GUI&#xff09;中创建框架&#xff0c;并提供各种边框样式和功能。它是Qt中一个基础的容器类&#xff0c;也是许多基础控件的基类&#xff0c;可以被QLCDNumber、QToolBox、…

数据重塑:长宽数据转换【基于tidyr】

在数据分析和可视化过程中&#xff0c;数据的组织形式直接影响着我们能够进行的分析类型和可视化效果。这里简单介绍两种常见的数据格式&#xff1a;长格式&#xff08;Long Format&#xff09;和宽格式&#xff08;Wide Format&#xff09;&#xff0c;以及如何使用tidyr包进行…

【网络】应用层——HTTP协议

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解什么是HTTP协议。 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不会坚持。早安! > 专栏选自&#xff1a;网络 &g…

【安全测试】sqlmap工具(sql注入)学习

前言&#xff1a;sqimap是一个开源的渗透测试工具&#xff0c;它可以自动化检测和利用SQL注入缺陷以及接管数据库服务器的过程。它有一个强大的检测引擎&#xff0c;许多适合于终极渗透测试的小众特性和广泛的开关&#xff0c;从数据库指纹、从数据库获 取数据到访问底层文件系…

【Android】webview常用方法和使用

文章目录 前言一、常见用法二、基础属性webView的常用方法WebViewClient的常用方法WebChromeClient的常用方法WebSettings的相关方法 三、加载流程和事件回调四、webview和JS之间的互相调用总结 五、参考链接 前言 最近项目又用到了webview&#xff0c;在回顾复习一次webview相…

可编辑PPT | 指挥中心系统建设与应用方案

本方案是一份全面的指挥中心系统建设与应用方案&#xff0c;涵盖了建设方案分析、指挥调度、远程通讯、会务管理等多个方面&#xff0c;旨在通过整合语音、视频监控、会议、指挥调度等多种技术&#xff0c;构建一个现代化、网络化、智慧化的城市指挥中心&#xff0c;以提高应对…

计算机新手练级攻略——善用搜索引擎

计算机学生新手练级攻略——善用搜索引擎 在信息爆炸的时代&#xff0c;计算机专业的学生如何有效地自我提升&#xff1f;答案可能就藏在一个简单却强大的工具——搜索引擎中。搜索引擎不仅是获取知识的入口&#xff0c;更是解决问题的利器。下面&#xff0c;我将分享一些善用搜…

vs2019托管调试助手 “ContextSwitchDeadlock“错误

错误描述 托管调试助手 "ContextSwitchDeadlock":“CLR 无法从 COM 上下文 0xd183e0 转换为 COM 上下文 0xd18328&#xff0c;这种状态已持续 60 秒。拥有目标上下文/单元的线程很有可能执行的是非泵式等待或者在不发送 Windows 消息的情况下处理一个运行时间非常长…

【ARM】MDK-烧录配置文件无权限访问

【更多软件使用问题请点击亿道电子官方网站】 1、 问题场景 客户代码编译正常、调试出现报错<Error: Flash Download failed - "Cortex-M4"> 仿真器识别正常&#xff0c;keil-Debug内显示相关信息、设备启动正常。 记录排查步骤&#xff0c;找到配置文件位…

【C语言刷力扣】66.加一

题目&#xff1a; 解题思路&#xff1a; 最初思路是打算将数组中的数提出来&#xff0c;加一&#xff0c;再放回另一数组中。后来发现数组最大长度100&#xff0c;而100位的数字太大了。 所有在数组上实现加一。 利用 carry 标记每一位是否进位&#xff0c;即该位数加 carry &a…

Docker使用docker-compose一键部署nacos、Mysql、redis

下面是一个简单的例子&#xff0c;展示如何通过Docker Compose文件部署Nacos、MySQL和Redis。请确保您的机器上已经安装了Docker和Docker Compose。 1&#xff0c;准备好mysql、redis、nacos镜像 sudo docker pull mysql:8 && sudo docker pull redis:7.2 &&…

【LLM】3:从零开始训练大语言模型(预训练、微调、RLHF)

一、 大语言模型的训练过程 预训练阶段&#xff1a;PT&#xff08;Pre training&#xff09;。使用公开数据经过预训练得到预训练模型&#xff0c;预训练模型具备语言的初步理解&#xff1b;训练周期比较长&#xff1b;微调阶段1&#xff1a;SFT&#xff08;指令微调/有监督微调…

YOLO即插即用---PConv

Run, Don’t Walk: Chasing Higher FLOPS for Faster Neural Networks 论文地址&#xff1a; 1. 论文解决的问题 2. 解决问题的方法 3. PConv 的适用范围 4. PConv 在目标检测中的应用 5. 评估方法 6. 潜在挑战 7. 未来研究方向 8.即插即用代码 论文地址&#xff1a; …

Fortran安装(vscode+gcc+Python)

编写时间&#xff1a; 2024年11月7日 环境配置&#xff1a; gcc VScode Python 条件&#xff1a; Windows 10 x64 VMware虚拟机 前言 这是我出的第2个关于Fortran安装的教程&#xff0c;由于上一个方法&#xff08;你可以在本专栏里找到&#xff09;对储存空间的要求比较…

ModuleNotFoundError: No module named ‘_ssl‘ centos7中的Python报错

报错 ModuleNotFoundError: No module named ‘_ssl’ 解决步骤&#xff1a; 1.下载openssl wget https://www.openssl.org/source/openssl-3.0.7.tar.gz tar -zxvf openssl-3.0.7.tar.gz cd openssl-3.0.72.编译安装 ./config --prefix/usr/local/openssl make make install3…

TensorFlow|猫狗识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 要求&#xff1a; 了解model.train_on_batch()并运用了解tqdm&#xff0c;并使用tqdm实现可视化进度条 &#x1f37b; 拔高&#xff08;可选&#xff09;&…