尧图精选

从零吃透 C++ 异常:抛出捕获、栈展开、异常重抛与编码规范详解

🕒 发布时间:2026/10/2 18:02:21 📁 来源:尧图网络
目录一.异常的概念及使用1.1异常的概念1.2异常的抛出和捕获1.3栈展开1.4查找匹配的处理代码1.5异常重新抛出1.6 异常安全问题1.7异常规范一.异常的概念及使用1.1异常的概念异常机制核心作用分离「错误检测」和「错误处理」异常把程序拆成两部分一段代码只负责检测有没有出错发现错误后把错误信息打包抛出另一段代码专门接收异常、处理错误检测代码不需要关心错误最终怎么处理实现解耦。通俗理解写代码时你预想一套流程输入合法数据、计算、输出结果。 但现实会出各种意外文件找不到、网络断了、用户输入乱填、除以 0、数组越界…… 这些不是你业务逻辑的正常结果是运行时出的意外状况就叫异常。区别错误异常运行时才发生可以被捕获、处理程序有机会继续跑。语法错误编译就报错代码根本跑不起来。异常 程序运行期间发生的非预期意外代表某种出错状态允许我们主动捕捉处理避免程序直接挂掉。1.2异常的抛出和捕获如果找到匹配 catch 捕获异常不会崩溃处理完 catch 之后继续执行 catch 后面的代码。如果没有任何 catch 捕获直接调用terminate()程序立刻挂掉根本不会走到 return。1.3栈展开情况 1有 catch 捕获异常不会挂void fun() { throw 出错; // 执行到这里本函数直接结束下面的return不会跑 cout这行不会执行; return; // ❗不会执行throw直接跳出函数 } int main() { try{ fun(); } catch(const char* e){ cout捕获异常eendl; } // catch处理完走到这里继续跑main后续代码 return 0; }throw一执行当前函数立刻退出return 被跳过不会执行发生栈展开销毁栈上局部对象向上找 catch找到 catch执行 catch 里面的处理catch 结束之后执行catch 之后的代码不是回到原来函数的 throw 后面。不是 “继续把函数跑完走到 return”函数早就提前退出了。情况 2没有 catch 捕获程序直接挂void fun() { throw 出错; return; // 依旧不执行 } int main() { fun(); // 没有try‑catch return 0; // ❗这行也不会执行程序直接terminate崩溃 }throw 抛出栈展开一路向上找到 main 还找不到匹配 catch直接终止程序main 的 return 也执行不到程序挂掉。通俗比喻函数就像跑任务return正常做完任务正常回家。throw任务中途出事直接撂挑子不干本函数直接跑路后面代码全部作废向上通知上层。如果上层有人接catch上层处理完继续跑上层剩下的代码。如果一层一层没人接直接整个程序停机崩溃。1.4查找匹配的处理代码// 一般大型项目程序才会使用异常下面我们模拟设计一个服务的几个模块 // 每个模块的继承都是Exception的派生类每个模块可以添加自己的数据 // 最后捕获时我们捕获基类就可以 class Exception { public: Exception(const string errmsg, int id) :_errmsg(errmsg) , _id(id) { } virtual string what() const { return _errmsg; } int getid() const { return _id; } protected: 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; } }; class HttpException : public Exception { public: HttpException(const string errmsg, int id, const string type) :Exception(errmsg, id) , _type(type) { } virtual string what() const { string str HttpException:; str _type; str :; str _errmsg; return str; } private: const string _type; }; void SQLMgr() { if (rand() % 7 0) { throw SqlException(权限不足, 100, select * from name 张三); } else { cout SQLMgr 调用成功 endl; } } void CacheMgr() { if (rand() % 5 0) { throw CacheException(权限不足, 100); } else if (rand() % 6 0) { throw CacheException(数据不存在, 101); } else { cout CacheMgr 调用成功 endl; } SQLMgr(); } void HttpServer() { if (rand() % 3 0) { throw HttpException(请求资源不存在, 100, get); } else if (rand() % 4 0) { throw HttpException(权限不足, 101, post); } else { cout HttpServer调用成功 endl; } CacheMgr(); } int main() { srand(time(0)); 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; }1.5异常重新抛出#includeiostream using namespace std; // 定义统一的异常基类包含错误信息和ID class Exception { public: Exception(const string errmsg, int id) :_errmsg(errmsg) , _id(id) { } // 关键点声明为虚函数支持多态调用 virtual string what() const { return _errmsg; } int getid() const { return _id; } protected: string _errmsg; int _id; }; // 具体的HTTP异常继承自Exception class HttpException : public Exception { public: HttpException(const string errmsg, int id, const string type) :Exception(errmsg, id) , _type(type) { } virtual string what() const { string str HttpException:; str _type; str :; str _errmsg; return str; } private: const string _type; }; // 模拟底层发送函数随机抛出网络错误或权限错误 void _SendMsg(const string s) { if (rand() % 2 0) { throw HttpException(网络不稳定发送失败, 102, put); } else if (rand() % 7 0) { throw HttpException(你已经不是对象的好友发送失败, 103, put); } else { cout 发送成功 endl; } } // 网络不稳定要求重试3次均失败 void SendMsg(const string s) { // 循环4次1次正常发送 3次重试 for (size_t i 0; i 4; i) { try { _SendMsg(s); // 走到这里代表成功了跳出循环 break; } catch (const Exception e) { // 核心逻辑根据错误ID判断 if (e.getid() 102) { // 102代表网络错误允许重试 if (i 3) // 重试次数耗尽重新抛出通知上层彻底失败 throw; cout 开始第 i 1 重试 endl; } else { // 重新抛出异常两种写法都可 //throw e; throw; } } } } int main() { srand(time(0)); string str; while (cin str) { try { SendMsg(str); } catch (const Exception e) { cout e.what() endl endl; } catch (...) { cout Unkown Exception endl; } } return 0; }1.6 异常安全问题double Divide(int a, int b) { try { // 当b 0时抛出异常 if (b 0) { string s(Divide by zero condition!); throw s; } else { return ((double)a / (double)b); } } catch (string errid) { cout errid endl; throw errid; } return 0; } void func() { // 申请资源 int* array new int[10]; int x, y; cin x y; // 中间调用了一系列函数 // 如果这里面的某个函数比如 Divide抛出了异常... Divide(x, y); // ...这里的 delete 就不会被执行导致内存泄漏 delete[] array; }1.7异常规范noexceptdouble Divide(int a, int b) { if (b 0) throw Division by zero!; // 这里明显会抛异常 return (double)a / b; } void Func() { int* array new int[10]; // new 可能会抛 bad_alloc // ... } int main() { // 测试 1Divide 函数内部有 throw所以结果是 0 (false) cout noexcept(Divide(1, 2)) endl; // 测试 2哪怕参数传的是安全的只要函数定义里有 throw结果还是 0 cout noexcept(Divide(1, 0)) endl; // 测试 3Func 内部调用了 newnew 是不安全的所以 Func 也是 0 cout noexcept(Func()) endl; // 测试 4i 这种简单操作绝对安全结果是 1 (true) int i 0; cout noexcept(i) endl; return 0; }
上一篇/下一篇内容由系统自动关联 返回资讯列表 →