C++异常处理深度解析:标准库异常类与最佳实践
C++异常处理深度解析:标准库异常类与最佳实践
思维导图概览
目录
- 异常处理基础
- 标准异常类体系
- 自定义异常类实现
- 异常规范详解
- 栈操作异常处理实例
- 异常处理最佳实践
- 常见问题与解决方案
1. 异常处理基础
异常处理是C++处理运行时错误的机制,核心组件包括:
try
:标识可能抛出异常的代码块throw
:抛出异常对象catch
:捕获并处理特定类型的异常
#include <iostream>
using namespace std;double divide(double a, double b) {if (b == 0) {throw "Division by zero!"; // 抛出异常}return a / b;
}int main() {try {cout << divide(10, 2) << endl;cout << divide(5, 0) << endl; // 将抛出异常}catch (const char* msg) { // 捕获异常cerr << "Error: " << msg << endl;}return 0;
}
输出结果:
5
Error: Division by zero!
2. 标准异常类体系
C++标准库提供了丰富的异常类,都继承自std::exception
基类:
主要异常类别
异常类别 | 包含的异常类 | 典型使用场景 |
---|---|---|
逻辑错误 | logic_error, invalid_argument | 程序逻辑错误 |
运行时错误 | runtime_error, range_error | 无法预测的外部条件 |
内存错误 | bad_alloc, bad_cast | 动态内存分配/类型转换失败 |
常用异常类详解
#include <stdexcept>
#include <vector>void testExceptions() {try {// 无效参数异常throw invalid_argument("Invalid parameter");}catch (const exception& e) {cerr << "Invalid argument: " << e.what() << endl;}try {// 越界访问异常vector<int> vec = {1, 2, 3};cout << vec.at(5); // 访问越界}catch (const out_of_range& e) {cerr << "Out of range: " << e.what() << endl;}try {// 内存分配失败int* hugeArray = new int[1000000000000];delete[] hugeArray;}catch (const bad_alloc& e) {cerr << "Memory allocation failed: " << e.what() << endl;}
}// 输出结果:
// Invalid argument: Invalid parameter
// Out of range: vector::_M_range_check: __n (which is 5) >= this->size() (which is 3)
// Memory allocation failed: std::bad_alloc
3. 自定义异常类实现
创建自定义异常类需继承std::exception
并重写what()
方法:
#include <exception>
#include <string>class StackException : public exception {string msg;
public:StackException(const string& message) : msg(message) {}virtual const char* what() const noexcept override {return msg.c_str();}
};class StackFullException : public StackException {
public:StackFullException() : StackException("Stack is full") {}
};class StackEmptyException : public StackException {
public:StackEmptyException() : StackException("Stack is empty") {}
};
4. 异常规范详解
动态异常规范(C++11前)
// 此函数只能抛出int类型异常
void fun1(char* s) throw(int) {if (s == nullptr) {throw 1; // 允许的}// throw "error"; // 编译通过但运行时调用unexpected()
}
C++11新规范
// 不抛出任何异常
void safeFunction() noexcept {// 不会抛出异常
}// 可能抛出特定异常
void riskyFunction() noexcept(false) {throw runtime_error("Something went wrong");
}// 条件性noexcept
template <typename T>
void swap(T& a, T& b) noexcept(noexcept(T())) {// 实现...
}
5. 栈操作异常处理实例
#include <iostream>
#include <stdexcept>
using namespace std;class Stack {enum { MAX_SIZE = 3 };int data[MAX_SIZE];int top = -1;int m_flag = 0; // 0: 正常, 1: 空栈, 2: 满栈public:void push(int value) {if (top >= MAX_SIZE - 1) {m_flag = 2; // 栈满throw StackFullException();}data[++top] = value;m_flag = 0;}int pop() {if (top < 0) {m_flag = 1; // 栈空throw StackEmptyException();}m_flag = 0;return data[top--];}int getStatus() const { return m_flag; }
};int main() {Stack s;try {s.push(10);s.push(20);s.push(30);cout << "Pushed 3 items" << endl;s.push(40); // 将抛出栈满异常}catch (const StackFullException& e) {cerr << "Error: " << e.what() << endl;cout << "Stack status: " << s.getStatus() << " (full)" << endl;}try {cout << "Popped: " << s.pop() << endl;cout << "Popped: " << s.pop() << endl;cout << "Popped: " << s.pop() << endl;s.pop(); // 将抛出栈空异常}catch (const StackEmptyException& e) {cerr << "Error: " << e.what() << endl;cout << "Stack status: " << s.getStatus() << " (empty)" << endl;}return 0;
}
输出结果:
Pushed 3 items
Error: Stack is full
Stack status: 2 (full)
Popped: 30
Popped: 20
Popped: 10
Error: Stack is empty
Stack status: 1 (empty)
6. 异常处理最佳实践
-
按引用捕获异常:
try { /* ... */ } catch (const exception& e) { // 推荐:避免对象切片cerr << e.what() << endl; }
-
异常安全保证:
- 基本保证:操作失败后对象仍处于有效状态
- 强保证:操作要么成功要么保持原状态(事务性)
- 不抛保证:操作保证不抛出异常
-
资源管理:
// 使用RAII管理资源 class FileHandler {FILE* file; public:FileHandler(const char* filename) : file(fopen(filename, "r")) {if (!file) throw runtime_error("File open failed");}~FileHandler() { if (file) fclose(file); }// 其他方法... };
-
避免在析构函数中抛出异常:
~MyClass() noexcept { // 使用noexcepttry {// 清理代码...}catch (...) {// 记录日志但不传播异常} }
7. 常见问题与解决方案
问题类型 | 解决方案 |
---|---|
未捕获异常 | 添加全局异常处理器 |
异常导致资源泄漏 | 使用RAII模式管理资源 |
异常类型不匹配 | 使用继承层次和基类捕获 |
异常安全不足 | 实现强异常保证 |
异常处理性能开销 | 避免在性能关键路径使用异常 |
全局异常处理
#include <exception>
#include <cstdlib>void terminateHandler() {cerr << "Uncaught exception! Terminating..." << endl;abort();
}int main() {set_terminate(terminateHandler);throw runtime_error("This will be handled by terminate");return 0;
}
输出结果:
Uncaught exception! Terminating...
Aborted (core dumped)
总结思维导图
关键要点回顾:
- 异常处理是C++处理运行时错误的机制,优于错误码
- 标准库提供丰富的异常类,均继承自
std::exception
- 自定义异常类应继承标准异常类并重写
what()
方法- 动态异常规范(
throw(type)
)已弃用,推荐使用noexcept
- RAII模式是保证异常安全的关键技术
- 析构函数应声明为
noexcept
避免异常传播- 全局异常处理器可捕获未处理异常
掌握异常处理技术能显著提高程序的健壮性和可维护性。合理使用异常可以使错误处理代码与正常业务逻辑分离,提高代码可读性和可维护性。
原创技术笔记,转载需注明出处。更多系统编程内容持续更新中…