在C++程序运行过程中,当抛出的异常没有被任何catch块捕获时,就会产生未处理的异常,此时程序会触发默认的终止逻辑,大部分情况下会直接崩溃退出。这种情况在复杂的多模块程序中很容易出现,需要开发者提前做好应对方案。
未处理异常的默认处理机制
C++标准规定,当未处理的异常产生时,运行时会自动调用std::terminate函数。而std::terminate的默认行为是调用std::abort函数,直接终止程序,不会执行后续的栈展开和析构逻辑,可能会导致资源泄漏等问题。
以下是一段会产生未处理异常的示例代码:
#include <iostream>
#include <stdexcept>
// 抛出运行时异常的函数
void throw_error() {
throw std::runtime_error("测试未处理的异常");
}
int main() {
// 这里没有捕获throw_error抛出的异常
throw_error();
std::cout << "这行代码不会被执行" << std::endl;
return 0;
}
运行上述代码时,程序会在抛出异常后直接终止,不会输出后面的内容。
自定义未处理异常的处理函数
C++提供了std::set_terminate函数,允许开发者自定义std::terminate被调用时的处理逻辑,替代默认的std::abort行为。自定义的处理函数需要满足无返回值、无参数的要求。
我们可以通过以下步骤实现自定义处理:
- 定义符合要求的终止处理函数,可以在函数中记录异常信息、释放必要资源等
- 调用
std::set_terminate将自定义的终止处理函数注册到运行时 - 如果需要在处理函数中获取异常信息,可以结合
std::current_exception和std::rethrow_exception来捕获当前未处理的异常
以下是自定义未处理异常处理的示例代码:
#include <iostream>
#include <exception>
#include <stdexcept>
#include <typeinfo>
// 自定义的终止处理函数
void custom_terminate_handler() {
std::cout << "捕获到未处理的异常,开始执行自定义处理逻辑" << std::endl;
// 尝试获取当前的异常
if (std::current_exception()) {
try {
// 重新抛出异常以便捕获
std::rethrow_exception(std::current_exception());
} catch (const std::exception& e) {
std::cout << "异常类型: " << typeid(e).name() << std::endl;
std::cout << "异常信息: " << e.what() << std::endl;
} catch (...) {
std::cout << "捕获到未知类型的异常" << std::endl;
}
} else {
std::cout << "没有可获取的异常信息" << std::endl;
}
// 处理完成后可以选择正常退出或者执行其他逻辑
std::cout << "自定义处理完成,程序退出" << std::endl;
exit(1);
}
// 抛出异常的测试函数
void test_func() {
throw std::runtime_error("这是自定义的运行时异常");
}
int main() {
// 注册自定义的终止处理函数
std::set_terminate(custom_terminate_handler);
// 调用会抛出异常但没有捕获的函数
test_func();
std::cout << "这行代码不会被执行" << std::endl;
return 0;
}
运行上述代码后,程序不会直接崩溃,而是会先执行custom_terminate_handler函数中的逻辑,输出异常相关信息后再退出。
处理未处理异常的最佳实践
虽然可以通过自定义std::terminate处理函数来应对未处理的异常,但最好的方式还是从根源上减少未处理异常的出现:
- 在程序的入口函数(如main函数)中添加顶层的catch块,捕获所有可能的异常,避免异常向上传递到运行时
- 对于可能抛出异常的函数,明确标注异常规格(如果使用noexcept,要确保函数内部不会抛出异常,否则会直接调用terminate)
- 在多线程程序中,每个线程的入口函数都要做好异常捕获,因为线程内未处理的异常会直接终止整个程序
- 自定义终止处理函数只适合做最后的兜底处理,比如记录崩溃日志、保存关键数据等,不要在其中做复杂的业务逻辑
不同场景下的注意事项
在以下场景中处理未处理异常需要额外注意:
析构函数中抛出异常
如果析构函数抛出了异常且没有在析构函数内部捕获,而此时又正在栈展开过程中(比如之前已经有异常抛出),程序会直接调用std::terminate,因此析构函数应该尽量保证不抛出异常,或者将异常在析构函数内部处理完毕。
noexcept函数中的异常
标记为noexcept的函数如果抛出了异常,不管是否被捕获,都会直接触发std::terminate,因此在给函数添加noexcept修饰时要确认函数内部不会抛出任何异常。
未处理的异常处理是C++程序稳定性保障的重要一环,开发者需要结合自定义终止处理和顶层异常捕获两种方式,最大程度减少程序异常崩溃的概率。
C++未处理异常异常处理terminate函数set_terminate修改时间:2026-06-13 10:33:58