AI提示词工程:从模糊描述到精准出图的四层转换法
2026/10/19 5:13:54
throw抛出一个异常对象(可是任意类型,推荐自定义异常类);catch语句捕获指定类型的异常,执行对应的处理逻辑;try包裹可能抛出异常的代码,后续紧跟一个或多个catch块,用于匹配异常分析:
基本语法格式:
/*----------------------------------------------------------------- try { // 可能抛出异常的代码 可能出错的函数(); } catch (异常类型1& e) { // 处理类型1异常 } catch (异常类型2& e) { // 处理类型2异常 } catch (...) { // 捕获任意类型异常(兜底处理) } -----------------------------------------------------------------*/最简示例(除零异常):
#include<exception> double Divide(int a, int b) { // 当 b == 0 时抛出异常 if (b == 0) { //string s("Divide by zero condition!"); //throw s; throw exception("Divide by zero condition!"); } else { return ((double)a / (double)b); } } void Func() { try { int len, time; cin >> len >> time; cout << Divide(len, time) << endl; } catch (const exception& e) { cout << e.what() << endl; } cout << "Func():" << __LINE__<< endl; } int main() { while (1) { try { Func(); } // 异常会先匹配最适配的 catch (const string& s) { cout << s << endl; } catch (const exception& e) { cout << e.what() << endl; } catch (...) // 任意类型的对象 { cout << "未知异常" << endl; } cout << "Func():" << __LINE__ << endl; } return 0; }抛出异常后,程序会暂停当前函数执行,沿调用链向上查找匹配的catch块,这个过程称为 “栈展开”:
try/catch块,若找到匹配的catch,则执行处理逻辑;catch;main函数仍未找到,调用terminate函数终止程序。补充:
catch子句,首先检查throw本身是否在try块内部,如果在则查找匹配的catch语句,如果有匹配的,则跳到catch的地方进行处理。try/catch子句,或者有try/catch子句但是类型不匹配,则退出当前函数,继续在外层调用函数链中查找,上述查找的catch过程被称为栈展开。main函数,依旧没有找到匹配的catch子句,程序会调用标准库的terminate函数终止程序。catch子句处理后,catch子句代码会继续执行。栈展开示例:
void Func1() { throw "Func1抛出异常"; // 抛出异常 } void Func2() { Func1(); // 调用Func1,不处理异常 } void Func3() { Func2(); // 调用Func2,不处理异常 } int main() { try { Func3(); // 调用Func3 } catch (const char* errmsg) { // 捕获Func1抛出的异常(栈展开:Func1→Func2→Func3→main) cout << "捕获异常:" << errmsg << endl; } return 0; }捕获异常时,遵循 “精确匹配优先、兼容转换次之” 的原则:
catch;int→const int);int[5]→int*);catch块,按顺序匹配,匹配成功后不再检查后续catch;catch (...)可捕获任意类型异常,通常作为兜底,避免程序终止。补充:
catch是类型完全匹配的,如果有多个类型匹配的,就选择离它位置更近的那个。main函数,异常仍旧没有被匹配就会终止程序,不是发生严重错误的情况下,我们是不期望程序终止的,所以一般main函数中最后都会使用catch(...),它可以捕获任意类型的异常,但是无法知道异常错误是什么。在大型项目中,直接抛出基本类型(如字符串、整数)的异常难以区分错误类型,推荐自定义异常类体系(基于继承),统一异常接口,便于管理和扩展。
代码实现:
#include<thread> // 一般大型项目程序才会使用异常,下面我们模拟设计一个服务的几个模块 // 每个模块的继承都是Expection的派生类,每个模块可以添加自己的数据 // 最后捕获的时候,我们捕获基类就可以,通过多态可以打印不同信息 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; }部分输出演示:
有时捕获异常后,无法完全处理(如仅记录日志),或需要根据错误类型分流处理,可通过throw;重新抛出异常,让外层调用链继续处理。
示例:网络请求重试
// 下面程序模拟展示了聊天时发送消息,发送失败补货异常,但是可能在 // 电梯地下室等场景手机信号不好,则需要多次尝试 // 如果多次尝试都发送不出去,则就需要捕获异常再重新抛出, // 其次如果不是网络差导致的错误,捕获后也要重新抛出。 void _SendMsg(const string& s) { if (rand() % 2 == 0) { throw HttpException("网络不稳定,发送失败",102, "put"); } else if (rand() % 7 == 0) { throw HttpException("你已经不是对方的好友,发送失败", 102, "put"); } else { cout << "发送成功" << endl; } } // 网络不稳定,要求重试三次,均失败 void SendMsg(const string& s) { for (size_t i = 0; i < 4; i++) { try { _SendMsg(s); // 走到这里,如果没有抛异常导致结束 // 那就代表成功了,可以执行到这个break,跳出循环 break; } catch (const Exception& e) { if (e.getid() == 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; }异常抛出后,当前函数后续代码不再执行,若之前申请了资源(内存、锁、文件句柄),未及时释放会导致资源泄漏,这是异常使用的核心痛点。解决方案:
catch中释放资源后重新抛出异常;示例:
double Divide(int a, int b) { // 当b == 0时抛出异常 if (b == 0) { throw "Division by zero condition!"; } return (double)a / (double)b; } void Func() { // 这里可以看到如果发生除0错误抛出异常,那下面的array就没有得到释放。 // 所以这里捕获异常后并不处理异常, // 异常还是交给外层处理,这里捕获了再重新抛出去。 int* array = new int[10]; int len, time; cin >> len >> time; try { cout << Divide(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; } catch (...) { cout << "Unkown Exception" << endl; } return 0; }C++11 提供noexcept关键字,用于声明函数是否会抛出异常,帮助编译器优化代码:
函数声明 noexcept:表示函数不会抛出异常;函数声明 noexcept(表达式):表达式为true时,证明该函数不抛异常(主要是用来确认和验证);noexcept的函数实际抛出异常,程序会调用terminate终止(根本没有机会捕获)补充:
throw(),表示函数不抛异常;函数参数列表的后面接throw(类型1, 类型2...),表示可能会抛出多种类型的异常,可能会抛出的类型用逗号分割。noexcept表示不会抛出异常,啥都不加表示可能会抛出异常。noexcept,也就是说如果一个函数用noexcept修饰了,但同时又包含了throw语句或者调用的函数可能会抛出异常,编译器还是会顺利编译通过(有些编译器可能会报个警告)。但是一个声明了noexcept的函数抛出了异常,程序会调用terminate终止程序。noexcept(expression)还可以作为一个运算符去检测一个表达式是否会抛出异常,可能会则返回false,不会就返回true。实际示例:
// C++11标记不会抛异常的方法 // double Divide(int a, int b) noexcept // C++98用来标记会抛异常的方法 // double Divide(int a,int b) throw(const char*) // C++98 // 这里表示这个函数只会抛出bad_alloc的异常 // void* operator new (std::size_t size) throw (std::bad_alloc); // 这里表示这个函数不会抛出异常 // void* operator delete (std::size_t size, void* ptr) throw(); // C++11 // size_type size() const noexcept; // iterator begin() noexcept; // const_iterator begin() const noexcept; double Divide(int a, int b) noexcept { // 当b == 0时抛出异常 if (b == 0) { throw "Division by zero condition!"; } return (double)a / (double)b; } int main() { try { int len, time; cin >> len >> time; cout << Divide(len, time) << endl; } catch (const char* errmsg) { cout << errmsg << endl; } catch (...) { cout << "Unkown Exception" << endl; } int i = 0; cout << noexcept(Divide(1, 2)) << endl; cout << noexcept(Divide(1, 0)) << endl; cout << noexcept(++i) << endl; return 0; }C++ 标准库提供了一套预定义的异常继承体系,基类为std::exception,派生类对应不同类型的标准异常(如内存分配失败、数组越界),可直接使用或继承扩展。
标准库异常体系核心类:
| 异常类 | 用途 | 错误信息获取方式 |
|---|---|---|
std::exception | 所有标准异常的基类 | what()(虚函数) |
std::bad_alloc | new分配内存失败时抛出 | what()返回 “bad alloc” |
std::out_of_range | 数组/容器越界时抛出 | what()返回越界信息 |
std::invalid_argument | 无效参数时抛出 | what()返回参数错误信息 |
std::exception就OK了