目录
二、异常处理的基础操作:从 try-catch 到 RAII
一、引言:程序世界的 "防暴警察"
想象一下,你正在驾驶一辆满载乘客的大巴车,突然遇到前方道路塌陷。如果没有刹车系统,大巴会直接冲进坑里;如果有刹车系统但没有安全带,乘客会在急刹车时受伤。C++ 异常处理机制就像是程序世界的 "刹车 + 安全带" 组合,让你在遇到意外时能够安全停车并保护资源。
在 C++ 中,异常处理是一把双刃剑:用得好可以写出健壮的代码,用得不好会导致程序崩溃或性能下降。本文将通过 20 个实战案例,带你从基础到高级全面掌握 C++ 异常处理的核心技术,包括:
- 异常处理的底层原理
- 自定义异常体系设计
- 多线程环境下的异常处理
- 性能优化策略
- 与设计模式的结合
- 实际项目中的最佳实践
二、异常处理的基础操作:从 try-catch 到 RAII
1. 异常处理的基本语法
try {
// 可能抛出异常的代码
if (y == 0) throw "除0错误";
} catch (const char* e) {
// 处理异常
cerr << "Error: " << e << endl;
}
- throw:抛出异常对象(可以是任意类型)
- try-catch:捕获并处理异常
- 栈展开(Stack Unwinding):异常抛出后,自动调用栈中对象的析构函数
2. 异常匹配的四大原则
- 类型精确匹配:
catch(int)
只能捕获int
类型异常 - 基类捕获派生类:
catch(Exception&)
可以捕获所有派生异常 - 数组和指针的特殊处理:
catch(int*)
无法捕获int[5]
- 引用和值的区别:
catch(Exception)
会切割对象,丢失派生类信息
3. 资源管理的终极方案:RAII
class File {
public:
File(const string& path) { /* 打开文件 */ }
~File() { /* 关闭文件 */ }
};
void readFile() {
File file("data.txt"); // 自动打开
// 业务逻辑
} // 作用域结束自动关闭
- RAII(Resource Acquisition Is Initialization):资源获取即初始化
- 优势:自动管理资源,避免内存泄漏
- 实战案例:用户代码中的
div1
函数存在内存泄漏,可改用 RAII:
void div1(int x, int y) {
unique_ptr<int[]> p(new int[1024*1024*10]); // 自动释放
if (y == 0) throw "除0错误";
cout << x/(double)y << endl;
}
三、自定义异常体系设计:打造专业级错误处理
4. 异常类的层次结构
class Exception {
public:
Exception(const string& msg) : _msg(msg) {}
virtual string what() const { return _msg; }
protected:
string _msg;
};
class SqlException : public Exception {
public:
SqlException(const string& sql) : Exception("SQL Error"), _sql(sql) {}
string what() const override { return _msg + " -> " + _sql; }
private:
string _sql;
};
- 设计原则:
- 所有异常继承自基类
Exception
- 提供
what()
方法返回错误信息 - 派生类包含特定错误数据
- 所有异常继承自基类
5. 用户代码中的异常体系解析
class HttpServerException : public Exception {
public:
HttpServerException(const string& type, const string& msg)
: Exception(msg), _type(type) {}
string what() const override {
return "HttpServerException: " + _type + " -> " + _msg;
}
private:
string _type;
};
- 优势:
- 统一错误处理接口
- 支持多态捕获
- 方便扩展新异常类型
6. 异常处理的三大安全级别
- 基本保证(Basic Guarantee):对象处于有效状态,无资源泄漏
- 强保证(Strong Guarantee):操作要么成功,要么回滚
- 无抛出保证(No-throw Guarantee):函数绝对不会抛出异常
四、多线程环境下的异常处理:从坑到填
7. 多线程异常的四大陷阱
- 异常无法跨线程传播:子线程抛出的异常无法被主线程捕获
- 资源竞争:多个线程同时访问共享资源
- 线程局部存储(TLS):每个线程独立的异常存储
- 异常与信号的冲突:信号可能中断异常处理流程
8. 多线程异常处理的实战方案
#include <future>
void worker() {
try {
// 业务逻辑
throw runtime_error("Worker error");
} catch (...) {
// 保存异常到线程局部存储
thread_local exception_ptr error = current_exception();
}
}
int main() {
thread t(worker);
t.join();
// 检查线程局部存储中的异常
if (thread_local_error) {
try {
rethrow_exception(thread_local_error);
} catch (const exception& e) {
cerr << "Caught: " << e.what() << endl;
}
}
return 0;
}
- 关键技术:
std::future
和std::promise
传递异常- 线程局部存储(
thread_local
) std::exception_ptr
保存异常信息
9. 用户代码中的多线程异常处理
int main() {
while (1) {
this_thread::sleep_for(chrono::seconds(1));
try {
HttpServer();
} catch (const Exception& e) {
cout << e.what() << endl;
} catch (...) {
cout << "Unknown Exception" << endl;
}
}
}
- 分析:
- 主线程循环处理异常
- 使用
this_thread::sleep_for
模拟服务器运行 - 捕获基类异常和未知异常
五、性能优化:在优雅与效率间寻找平衡
10. 异常处理的性能开销
操作 | 开销(相对于无异常) |
---|---|
抛出异常 | 10-100 倍 |
栈展开 | 5-50 倍 |
类型匹配 | 1-10 倍 |
构造异常对象 | 1-5 倍 |
11. 性能优化的五大策略
-
避免不必要的异常:
// 反模式:用异常处理常规错误
int divide(int a, int b) {
if (b == 0) throw runtime_error("Divide by zero");
return a / b;
}
// 优化:返回错误码
pair<int, bool> divide(int a, int b) {
if (b == 0) return {0, false};
return {a / b, true};
}
2. 使用 noexcept 声明:
void sort(vector<int>& v) noexcept {
// 保证不抛出异常
}
3. 轻量级异常类:
class LightException {
public:
LightException(const string& msg) : _msg(msg) {}
// 避免复杂的析构函数
private:
string _msg;
};
4.延迟异常处理:
void process() {
try {
// 业务逻辑
} catch (...) {
// 记录日志,不立即处理
log_error("Exception occurred");
throw; // 重新抛出
}
}
5.异常对象重用:
const runtime_error divide_by_zero("Divide by zero");
void divide(int a, int b) {
if (b == 0) throw divide_by_zero;
}
六、与设计模式的结合:打造可维护的代码
12. 工厂模式创建异常
class ExceptionFactory {
public:
static unique_ptr<Exception> createException(const string& type) {
if (type == "sql") {
return make_unique<SqlException>("select * from users");
} else if (type == "http") {
return make_unique<HttpServerException>("get", "Not Found");
}
return nullptr;
}
};
13. 策略模式处理异常
class ExceptionHandler {
public:
virtual void handle(const Exception& e) = 0;
};
class LogHandler : public ExceptionHandler {
public:
void handle(const Exception& e) override {
cout << "Log: " << e.what() << endl;
}
};
class EmailHandler : public ExceptionHandler {
public:
void handle(const Exception& e) override {
send_email("admin@example.com", e.what());
}
};
14. 模板元编程处理异常
template<typename T>
void process() {
try {
T::do_something();
} catch (const exception& e) {
// 处理T类型的异常
}
}
七、实战案例:服务器开发中的异常处理
15. 用户代码的异常体系分析
void HttpServer() {
srand(time(0));
if (rand() % 3 == 0) {
throw HttpServerException("get", "Not Found");
} else if (rand() % 4 == 0) {
throw HttpServerException("post", "Unauthorized");
}
CacheMgr();
}
- 设计亮点:
- 模拟真实服务器的异常场景
- 异常类型覆盖不同业务模块
- 多层调用链中的异常传播
16. 异常处理的最佳实践
日志记录:
catch (const Exception& e) {
ofstream log("error.log", ios::app);
log << e.what() << endl;
}
重试机制:
bool connect_server() {
for (int i = 0; i < 3; ++i) {
try {
// 连接服务器
return true;
} catch (const NetworkException& e) {
cerr << "Retry " << i+1 << ": " << e.what() << endl;
this_thread::sleep_for(chrono::seconds(1));
}
}
throw runtime_error("Failed to connect");
}
异常隔离:
void process_request() {
try {
// 处理请求
} catch (...) {
// 隔离异常,避免影响其他请求
reset_request_state();
}
}
八、高级话题:从原理到优化
17. 异常处理的底层实现
-
异常对象的创建:
- 栈上对象:直接复制
- 堆上对象:需手动管理
- 栈展开过程:
void func3() { throw 42; }
void func2() { func3(); }
void func1() { func2(); }
int main() {
try {
func1();
} catch (int e) {
cout << "Caught: " << e << endl;
}
}
- 栈展开顺序:func3 () → func2 () → func1 () → main ()
-
异常与 RTTI:
dynamic_cast
和typeid
在异常处理中的应用- 性能开销:RTTI 会增加可执行文件大小
18. 异常与其他语言的对比
语言 | 异常类型检查 | 资源管理 | 性能开销 |
---|---|---|---|
C++ | 运行时检查 | RAII | 高 |
Java | 编译时检查 | 自动垃圾回收 | 中 |
Python | 运行时检查 | 自动垃圾回收 | 低 |
19. 编译器优化与异常
- -fno-exceptions:禁用异常处理
- -fno-unwind-tables:减少栈展开信息
- -O3:优化异常处理代码
20.异常的黄金法则
最后,记住异常处理的黄金法则:
- 异常用于处理 "意外",而不是常规流程
- 资源管理优先使用 RAII
- 异常类型设计要层次分明
- 多线程环境下使用线程局部存储
- 性能敏感代码避免使用异常
异常练手代码
#include <bits/stdc++.h>
#include <thread>
using namespace std;
//struct AA
//{
// AA()
// {
// cout << "AA" << endl;
// }
// ~AA()
// {
// cout << "~AA" << endl;
// }
//};
//
//void div1(int x, int y)
//{
// int* p = new int[1024 * 1024 * 10];
// if (y == 0)
// {
// throw "除0错误";
// }
// else
// {
// cout << x / (double)y << endl;
// delete[] p;
// cout << "delete[]" << endl;
// }
//}
//
//int main()
//{
// int x = 0, y = 0;
// cin >> x >> y;
// try
// {
// div1(x, y);
// }
// catch (const char* str)
// {
// cout << str << endl;
// }
// return 0;
//}
//double Division(int a, int b)
//{
// // 当b == 0时抛出异常
// if (b == 0)
// {
// throw "Division by zero condition!";
// }
// return (double)a / (double)b;
//}
//void Func()
//{
// int* array = new int[10];
// try
// {
// int len, time;
// cin >> len >> time;
// cout << Division(len, time) << endl;
// }
// catch (...)
// {
// cout << "delete []" << array << endl;
// delete[] array;
// throw;
// }
// // ...
// cout << "delete []" << array << endl;
// delete[] array;
//}
//int main()
//{
// try
// {
// Func();
// }
// catch (const char* errmsg)
// {
// cout << errmsg << endl;
// }
// return 0;
//}
// 服务器开发中通常使用的异常继承体系
// 定义了一个异常基类
class Exception
{
public:
Exception(const string& errmsg, int id)
:_errmsg(errmsg)
, _id(id)
{}
// 可以在外部显示的查看错误信息是什么
virtual string what() const
{
return _errmsg;
}
protected:
// 通常包括有异常信息和抛出异常所对应的id号码
string _errmsg;
int _id;
};
// 由基类异常继承而来的:数据库异常
class SqlException : public Exception
{
public:
SqlException(const string& errmsg, int id, const string& sql)
:Exception(errmsg, id)
, _sql(sql)
{}
// 对基类中的输出信息进行对应的改造,使得输出对应的错误信息
virtual string what() const
{
string str = "SqlException:";
str += _errmsg;
str += "->";
str += _sql;
return str;
}
private:
// 在数据库异常中新增了和数据库有关的信息
const string _sql;
};
// 缓存区异常
class CacheException : public Exception
{
public:
CacheException(const string& errmsg, int id)
:Exception(errmsg, id)
{}
virtual string what() const
{
string str = "CacheException:";
str += _errmsg;
return str;
}
};
// web服务器异常
class HttpServerException : public Exception
{
public:
HttpServerException(const string& errmsg, int id, const string& type)
:Exception(errmsg, id)
, _type(type)
{}
virtual string what() const
{
string str = "HttpServerException:";
str += _type;
str += ":";
str += _errmsg;
return str;
}
private:
const string _type;
};
// 模拟数据库中的命令
void SQLMgr()
{
srand(time(0));
if (rand() % 7 == 0)
{
throw SqlException("权限不足", 100, "select * from name = '张三'");
}
}
void CacheMgr()
{
srand(time(0));
if (rand() % 5 == 0)
{
throw CacheException("权限不足", 100);
}
else if (rand() % 6 == 0)
{
throw CacheException("数据不存在", 101);
}
SQLMgr();
}
void HttpServer()
{
srand(time(0));
if (rand() % 3 == 0)
{
throw HttpServerException("请求资源不存在", 100, "get");
}
else if (rand() % 4 == 0)
{
throw HttpServerException("权限不足", 101, "post");
}
CacheMgr();
}
int main()
{
while (1)
{
this_thread::sleep_for(chrono::seconds(1));
try
{
HttpServer();
}
catch (const Exception& e)
{
cout << e.what() << endl;
}
catch (...)
{
cout << "Unkown Exception" << endl;
}
}
return 0;
}