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 { // 使用noexcept try { // 清理代码... } 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避免异常传播- 全局异常处理器可捕获未处理异常
掌握异常处理技术能显著提高程序的健壮性和可维护性。合理使用异常可以使错误处理代码与正常业务逻辑分离,提高代码可读性和可维护性。
原创技术笔记,转载需注明出处。更多系统编程内容持续更新中…
4268

被折叠的 条评论
为什么被折叠?



