C++异常处理应抛出std::exception派生对象,catch按派生到基类顺序书写,禁用try-catch控制正常流程,析构函数不得throw,业务层优先用std::expected而非异常。
很多初学者会写 throw 42 或 throw "error",这在语法上合法但极不稳健。C++ 标准推荐抛出继承自 std::exception 的对象(如 std::runtime_error),否则 catch 块难以统一处理,且无法调用 .what() 获取描述。
throw std::runtime_error("file not found")
throw "file not found"(C 字符串字面量,生命周期短,catch 中可能悬垂)throw 42 虽能被 catch(int) 捕获,但丢失上下文、不可扩展、无法携带堆栈信息C++ 的 catch 匹配是静态、顺序匹配的。如果把 catch(const std::exception&) 写在前面,后面所有派生类(如 std::logic_error)都将被它“吃掉”,导致更具体的错误处理逻辑永远不执行。
catch(const std::invalid_argument&) → catch(const std::runtime_error&) → catch(const std::exception&)
catch(const std::exception&) 放最前 → 所有子类异常都进不来catch(...) 除非你真要兜底日志+终止,它捕获一切(包括信号、硬件异常),且无类型信息,极易掩盖问题异常机制设计初衷是处理「罕见、意外、无法局部恢复」的错误,不是替代条件分支。滥用会导致性能骤降(即使没抛出,某些编译器仍需维护栈展开表)、代码可读性崩坏、静态分析工具失效。
try { parse_int(s); } catch(...) { return false; } —— 应该用 std::
from_chars 或预检查new(std::nothrow) 不够用时)std::out_of_range”,调用方就必须处理;若只靠异常传递业务状态(如“用户不存在”),就混淆了错误与业务逻辑C++ 标准规定:若在栈展开过程中(即另一个异常正被处理时)析构函数又抛异常,std::terminate() 立即调用,进程退出。而析构函数常被隐式调用(如作用域结束、异常传播中自动析构局部对象),风险极高。
try/catch 吞掉异常(并记录日志)~MyResource() { close(fd); if (errno) throw std::system_error(errno, ...); }
noexcept 显式标注析构函数(如 ~MyClass() noexcept)不仅是文档说明,更是编译器优化和容器安全的前提std::expected 或 std::variant,往往比硬套 try-catch 更稳健。