40人参与 • 2026-07-19 • C/C++
try {
// 正常业务代码,可能抛出异常
throw 异常实例;
}
catch (异常类型1 参数) {
// 处理类型1的错误
}
catch (异常类型2 &参数) {
// 推荐引用捕获,避免对象拷贝切片
}
catch (...) {
// 万能捕获,捕获所有未匹配异常,兜底处理
}
try:划定「受监控代码块」,块内抛出的异常才会被后续 catch 捕获;throw:主动抛出异常对象,中断当前代码执行,触发栈展开;catch:匹配对应类型的异常,处理错误逻辑;无匹配 catch 程序直接调用 std::terminate 崩溃。c++ 对抛出对象无类型限制,基础类型、字符串、自定义类、标准异常类均可:
// 1. 抛出内置类型
throw -1;
throw false;
// 2. 抛出std::string
throw std::string("数组下标越界");
// 3. 工程推荐:抛出标准异常对象
throw std::runtime_error("除数不能为0");严格类型匹配,不做隐式转换抛出 int 无法被 double、long 的 catch 捕获;抛出子类不能先写父类 catch,否则子类永远无法匹配。错误示范:
try { throw std::out_of_range("越界"); }
// 父类在前,子类直接失效
catch(std::exception& e) {}
catch(std::out_of_range& e) {}
正确顺序:子类异常在前,基类异常在后。
捕获优先使用引用 &直接值捕获会拷贝异常对象,带来性能损耗;基类值捕获会发生对象切片,丢失子类独有的错误信息。标准写法:catch(std::exception &e)
catch(...) 万能捕获只能放在最后仅用于兜底、打印日志、统一释放资源,无法获取异常具体信息。

当执行 throw 抛出异常时,编译器会从抛出点反向遍历函数调用栈:
try 块内所有局部栈对象,自动调用析构函数;catch 块,停止栈展开,进入异常处理逻辑;catch,调用 std::terminate() 终止程序。 c 语言出错时必须手动 free、fclose,一旦提前 return 或出错,极易遗漏释放; c++ 局部对象出作用域自动析构,栈展开过程会强制执行析构,裸指针会泄漏,智能指针不会。
void badfunc() {
int* p = new int[100];
throw std::runtime_error("出错");
// 栈展开只销毁局部变量p(指针本身),不会释放堆内存,内存泄漏!
}
void goodfunc() {
std::unique_ptr<int[]> p(new int[100]);
throw std::runtime_error("出错");
// unique_ptr析构时自动delete堆内存,无泄漏
}结论:工程中禁止裸指针管理堆资源,必须使用 raii 容器 / 智能指针,保证异常安全。
所有标准异常均继承自顶层基类 std::exception,定义头文件 <stdexcept>,基类提供虚函数 virtual const char* what() const noexcept,返回字符串形式错误描述。
属于程序编写失误,正常测试场景应提前拦截,理论上不应该在线上抛出
invalid_argument:函数传入非法参数out_of_range:容器下标、数组越界访问domain_error:数学函数定义域非法(如负数开平方根)length_error:容器容量超出最大限制外部环境导致,无法单纯靠代码规避,线上高频出现
range_error:数值计算结果超出值域范围overflow_error:算术运算上溢underflow_error:算术运算下溢#include <iostream>
#include <stdexcept>
double divide(int a, int b) {
if (b == 0) {
// 运行时错误:除数为0
throw std::runtime_error("divide by zero error");
}
return static_cast<double>(a) / b;
}
int main() {
try {
divide(10, 0);
} catch (std::runtime_error &err) {
// what() 获取错误信息
std::cout << "捕获异常:" << err.what() << std::endl;
} catch (std::exception &err) {
// 兜底捕获所有标准异常
std::cout << "通用异常:" << err.what() << std::endl;
}
return 0;
}标准异常仅提供通用错误描述,大型项目需要区分模块错误(数据库异常、网络异常、文件异常),通过继承 std::exception 实现自定义异常。
#include <exception>
#include <string>
// 数据库模块专属异常
class dbexception : public std::exception {
private:
std::string err_msg;
public:
// 构造函数接收自定义错误信息
explicit dbexception(const std::string& msg) : err_msg(msg) {}
// 重写虚函数what,加noexcept保证不抛异常
const char* what() const noexcept override {
return err_msg.c_str();
}
};
// 使用
void sqlquery() {
bool connectfail = true;
if (connectfail) {
throw dbexception("数据库连接超时,端口无法访问");
}
}catch 时精准定位故障模块;早期 c++ 使用 throw(类型列表) 声明函数允许抛出的异常:
// 仅能抛出int、string void oldfunc() throw(int, std::string); // 承诺不抛出任何异常 void nothrowfunc() throw();
缺陷:编译器不做强制检查,运行时抛出未声明异常直接崩溃,可读性差,新标准彻底废弃。
noexcept:函数承诺不会抛出任何异常void safefunc() noexcept;
noexcept(布尔表达式):条件式无异常,表达式为 true 则等价 noexcepttemplate<typename t> void swap(t& a, t& b) noexcept(noexcept(a.swap(b)));
noexcept 的函数内部若抛出异常,程序直接调用 std::terminate,不会进入任何 catch;noexcept;noexcept。捕获异常后,有时需要先打印日志、释放临时资源,再将异常向上层业务传递,分为两种重抛方式:
throw; 无参数:原样重抛原有异常,保留完整子类信息(推荐)throw 新对象; 生成全新异常,丢失原有异常上下文try {
throw std::out_of_range("数组下标100越界");
} catch (std::exception &e) {
std::cout << "记录错误日志:" << e.what() << std::endl;
throw; // 重抛原始out_of_range异常,上层可精准捕获
}
编写异常安全代码分为三个等级,项目开发最低要求满足基础保证,核心业务建议强保证。
throw &localerr;,栈展开后局部对象销毁,外部捕获到野指针;直接抛出值对象。catch(...)防止程序直接闪退。到此这篇关于c++异常处理完整深度解析的文章就介绍到这了,更多相关c++异常处理内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论