【GESP】C++四级考试大纲知识点梳理, (11) 异常处理机制
终于到最后一条了,GESP C++四级官方考试大纲中,共有11条考点,本文针对第11条考点进行分析介绍。
(11)了解异常处理机制,掌握异常处理的常用方法。
其实,由于本人也是边学、边实验、边总结,且对考纲深度和广度的把握属于个人理解。因此本文更多的不是一个教程,而是个人知识梳理,如有遗漏、疏忽,欢迎指正、交流。
四级其他考点回顾:
- 【GESP】C++四级考试大纲知识点梳理, (1) 指针
- 【GESP】C++四级考试大纲知识点梳理, (2) 结构体和二维数组
- 【GESP】C++四级考试大纲知识点梳理, (3) 模块化和函数
- 【GESP】C++四级考试大纲知识点梳理, (4) 变量和作用域
- 【GESP】C++四级考试大纲知识点梳理, (5) 值传递
- 【GESP】C++四级考试大纲知识点梳理, (6) 递推算法
- 【GESP】C++四级考试大纲知识点梳理, (7) 排序算法基本概念
- 【GESP】C++四级考试大纲知识点梳理, (8) 冒泡、插入、选择排序
- 【GESP】C++四级考试大纲知识点梳理, (9) 简单算法复杂度的估算
- 【GESP】C++四级考试大纲知识点梳理, (10) 文件读写和重定向
在 C++ 中,异常处理机制主要用于处理程序运行时发生的异常情况,使程序具有更强的鲁棒性(容错性)和可维护性。
一、异常处理机制的定位和作用
C++ 中的异常处理机制是一种结构化错误处理机制,其定位和作用主要有:
✅ 1. 程序健壮性的重要保障
异常处理用于应对程序运行时的非正常情况,如除以 0、数组越界、内存不足、非法输入等。这些问题如果不处理,程序可能崩溃或输出错误结果。
✅ 2. 错误处理与业务逻辑解耦
异常处理将“如何应对错误”与“主流程的正常逻辑”分离,使代码结构更清晰、可维护性更强。
✅ 3. 支持跨层传播的错误传递机制
异常可以从函数内部“抛出”并在调用者(甚至更高层)中“捕获”,避免了每层函数都要显式返回错误码。
二、异常处理的常用方法
C++ 提供了标准的异常处理机制,主要通过 try
、catch
和 throw
关键字实现。
- throw:用于抛出异常,表示程序检测到错误或异常情况。
- try:定义可能抛出异常的代码块。
- catch:捕获并处理特定类型的异常。
2.1 基本异常处理
使用 try
和 catch
捕获特定类型的异常。通常,异常可以是内置类型(如 int
、std::string
)或自定义异常类。
代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <string>
int divide(int a, int b) {
if (b == 0) {
throw std::string("Division by zero is not allowed");
}
return a / b;
}
int main() {
try {
int result = divide(10, 0);
std::cout << "Result: " << result << std::endl;
} catch (const std::string& e) {
std::cerr << "Error: " << e << std::endl;
}
return 0;
}
说明:
throw
抛出std::string
类型的异常。catch
捕获std::string
类型的异常并打印错误信息。- 如果没有异常,
try
块中的代码正常执行。
2.2 捕获多种异常类型
可以通过多个 catch
块捕获不同类型的异常。C++ 允许按异常类型的层次结构进行匹配。
代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <stdexcept>
void processValue(int value) {
if (value < 0) {
throw std::invalid_argument("Negative value not allowed");
} else if (value > 100) {
throw std::out_of_range("Value exceeds maximum limit");
}
std::cout << "Value processed: " << value << std::endl;
}
int main() {
try {
processValue(-5);
processValue(150);
} catch (const std::invalid_argument& e) {
std::cerr << "Invalid argument: " << e.what() << std::endl;
} catch (const std::out_of_range& e) {
std::cerr << "Out of range: " << e.what() << std::endl;
} catch (...) {
std::cerr << "Unknown error occurred" << std::endl;
}
return 0;
}
说明:
- 使用标准库中的异常类(如
std::invalid_argument
和std::out_of_range
)。 catch (...)
是通配符捕获,用于处理未明确指定的异常类型。- 异常按声明顺序匹配,因此更具体的异常类型应放在前面。
2.3 自定义异常类
对于复杂程序,建议定义自定义异常类,继承自 std::exception
或其子类,以便提供更详细的错误信息。
代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <stdexcept>
#include <string>
#include <cmath>
class MyException : public std::exception {
private:
std::string message;
public:
MyException(const std::string& msg) : message(msg) {}
const char* what() const noexcept override {
return message.c_str();
}
};
double calculateSquareRoot(double x) {
if (x < 0) {
throw MyException("Cannot calculate square root of a negative number");
}
return sqrt(x);
}
int main() {
try {
double result = calculateSquareRoot(-4.0);
std::cout << "Square root: " << result << std::endl;
} catch (const MyException& e) {
std::cerr << "Custom error: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Standard error: " << e.what() << std::endl;
} catch (...) {
std::cerr << "Unknown error" << std::endl;
}
return 0;
}
说明:
MyException
继承自std::exception
,重写了what()
方法以提供自定义错误信息。- 异常类可以通过成员变量存储额外信息,增强错误描述的灵活性。
- 多个
catch
块按继承层次匹配异常。
2.4 异常规范(C++11 前)与 noexcept(C++11 后)
- 异常规范(C++11 前):C++98/03 使用
throw()
动态异常规范来指定函数可能抛出的异常类型,但此特性在 C++11 中被废弃。 - noexcept(C++11 后):用于声明函数不会抛出异常,优化性能并提高代码安全性。
代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
// 声明函数不会抛出异常
void safeFunction() noexcept {
std::cout << "This function does not throw exceptions" << std::endl;
}
void mayThrow() {
throw std::runtime_error("An error occurred");
}
int main() {
try {
safeFunction();
mayThrow();
} catch (const std::exception& e) {
std::cerr << "Caught: " << e.what() << std::endl;
}
return 0;
}
执行结果:
1
2
This function does not throw exceptions
Caught: An error occurred
说明:
noexcept
修饰的函数若抛出异常,将导致程序调用std::terminate()
。- 使用
noexcept
可以提高编译器优化效率,特别是在模板代码中。
2.5 栈展开与 RAII
C++ 的异常处理会触发栈展开(stack unwinding),即从抛出异常的点到捕获异常的 catch
块之间,局部对象的析构函数会被调用。这使得 RAII(资源获取即初始化)技术在异常处理中尤为重要。
代码示例:RAII 与异常处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <stdexcept>
class Resource {
public:
Resource() { std::cout << "Resource acquired" << std::endl; }
~Resource() { std::cout << "Resource released" << std::endl; }
};
void riskyOperation() {
Resource res; // RAII 对象
std::cout << "Inside riskyOperation" << std::endl;
throw std::runtime_error("Something went wrong");
}
int main() {
try {
riskyOperation();
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
return 0;
}
代码输出:
1
2
3
4
Resource acquired
Inside riskyOperation
Resource released
Error: Something went wrong
说明:
Resource
对象的析构函数在异常抛出时自动调用,确保资源正确释放。- RAII 避免了资源泄漏问题,特别是在复杂控制流中。
三、标准异常类
C++ 标准库提供了 std::exception
层次结构的异常类,常用类包括:
std::logic_error
:逻辑错误,如std::invalid_argument
、std::out_of_range
。std::runtime_error
:运行时错误,如std::overflow_error
、std::underflow_error
。std::bad_alloc
:内存分配失败。std::bad_cast
:动态类型转换失败。
使用标准异常类的好处在于:
- 可移植性:标准异常类在所有支持 C++ 的平台上都可用,代码可以轻松地在不同编译器和操作系统间移植
- 可读性:标准异常类的命名清晰直观(如
std::invalid_argument
、std::out_of_range
等),其他程序员一看就能理解异常的类型和含义 - 一致性:使用标准异常类可以与 C++ 标准库保持一致的错误处理风格
四、异常处理的最佳实践
- 优先使用标准异常类:除非有特殊需求,否则使用
std::exception
及其子类。 - 捕获异常时使用引用:避免对象拷贝,使用
const std::exception&
。 - 按需捕获异常:避免滥用
catch (...)
,因为它可能掩盖未预期的错误。 - 确保资源安全:使用 RAII 管理资源,避免异常导致的资源泄漏。
- 明确异常语义:抛出异常时提供清晰的错误信息。
- 谨慎使用 noexcept:仅在确定函数不会抛出异常时使用。
五、总结
C++ 的异常处理机制通过 try
、catch
和 throw
提供了一种强大的错误处理方式。常用方法包括基本异常处理、捕获多种异常类型、自定义异常类、使用 noexcept
优化性能以及结合 RAII 确保资源安全。遵循最佳实践(如使用标准异常类、避免滥用通配符捕获)可以编写健壮且可维护的代码。
所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code
GESP各级别考纲、真题讲解、知识拓展和练习清单等详见【置顶】【GESP】C++ 认证学习资源汇总
“luogu-”系列题目已加入洛谷Java、C++初学团队,作业清单,可在线评测,团队名额有限。
“bcqm-”系列题目可在编程启蒙题库进行在线评测。
欢迎加入:Java、C++、Python技术交流QQ群(982860385),大佬免费带队,有问必答
欢迎加入:C++ GESP/CSP认证学习QQ频道,考试资源总结汇总
欢迎加入:C++ GESP/CSP学习交流QQ群(688906745),考试认证学员交流,互帮互助