跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 异常处理机制详解:抛出、捕获与安全规范

C++ 异常处理机制,涵盖基本语法(try/throw/catch)、异常匹配原则及函数调用链传播规则。重点讲解了异常安全注意事项,指出构造和析构函数应避免抛异常以防资源泄漏。同时对比了旧版异常规范与 C++11 的 noexcept 关键字,并列举了标准库提供的各类异常类及其用途,帮助开发者构建健壮的异常处理体系。

Pythonist发布于 2026/3/30更新于 2026/10/24118 浏览
C++ 异常处理机制详解:抛出、捕获与安全规范

C 语言处理错误

  • 终止程序:利用 assert() 断言去终止程序,当 () 的表达结果为 false 时会终止程序。
  • 返回错误码:手动查找对应的错误,系统的接口函数将作错误码放到 errno 中表示错误。

C 语言中的 strerror 将参数对应 errno 的错误信息的字符串返回。errno 是一个全局变量,当使用标准库的函数发生错误时,就会将对应的错误码放到 errno 中,每个错误码对应着不同的错误信息,strerror 就可以将错误码对应的字符串返回。

以下为错误码 0~10 对应的信息:

#include <iostream>
#include <errno.h>
using namespace std;

int main() {
    for (size_t i = 0; i <= 10; ++i)
        cout << i << ":" << strerror(i) << endl;
    return 0;
}

错误码示例

C++ 异常

C++ 的异常处理是一种应对程序运行时错误的机制,允许在程序中独立开发的部分在运行时就出现的错误进行通信并作出相应的处理,使得将问题的检测与解决问题的过程分开,程序的一部分检测问题的出现,然后将解决任务传递给程序的另一部分。

总的来说,异常能够提供一种结构化的方法来捕获和处理错误,从而提高代码的健壮性和可维护性。

异常的抛出与捕获

基本语法

C++ 的异常处理依赖于三个关键字:

  • try:用于定义可能抛出异常的代码块。
  • throw:用于抛出异常对象。
  • catch:用于捕获异常,并定义处理异常的逻辑。

使用实例:

void testException {
     (x == )  ; 
    cout <<  << x << endl;
}

{
     {
        ();
    }  ( * e) {
        cout <<  << e << endl;
    }
     ;
}
(int x)
if
0
throw
"Division by zero error!"
// 抛出 const char* 类型的异常
"x:"
int main()
try
testException
0
catch
const
char
"Caught an exception: "
return
0

catch 的匹配原则

1. 异常的类型

C++ 支持抛出任意类型的对象作为异常,例如:

  • 基本类型:如 int、char、float。
  • 标准库类型:如 string。
  • 自定义类型:通过类和结构体定义的类型。

示例:

throw 42; // 抛出整数
throw std::string("Error"); // 抛出字符串
throw std::runtime_error("Runtime error"); // 抛出标准库异常类

异常类型示例

2. 标准异常类

C++ 标准库还提供了一组异常类,位于 exception 头文件中。常见的标准异常类包括:

  • exception:所有标准异常的基类。
  • runtime_error:运行时错误。
  • logic_error:逻辑错误,如非法参数、越界访问等。
  • 其他派生类:如 out_of_range、invalid_argument。

使用标准异常类:

int main() {
    try {
        throw runtime_error("Runtime exception occurred!");
    } catch (const exception& e) {
        cout << "Caught standard exception: " << e.what() << endl;
    }
    return 0;
}

3. 匹配原则

  • 异常抛出的对象的类型决定了应该匹配哪个 catch 的处理代码,该过程不会有隐式类型转换。
void testException(int x) {
    if (x == 0) throw "Division by zero error!"; // 抛出 const char* 类型的异常
    cout << "x:" << x << endl;
}

int main() {
    try {
        testException(0);
    } catch (const string& e) // 不会有隐式类型转换,不会匹配 string 的 catch
    {
        cout << "Caught an exception(string): " << e << endl;
    }
    catch (const char* e) {
        cout << "Caught an exception: " << e << endl;
    }
    return 0;
}

类型不匹配示例

  • 如果有多个类型匹配的 catch 的版本,则会去调用离抛出异常位置最近的 catch。
void testException(int x) {
    if (x == 0) throw "Division by zero error!"; // 抛出 const char* 类型的异常
    cout << "x:" << x << endl;
}

void test1() {
    try {
        testException(0);
    } catch (const char* e) {
        cout << "(most near) Caught an exception: " << e << endl;
    }
}

int main() {
    try {
        test1();
    } catch (const char* e) {
        cout << "Caught an exception: " << e << endl;
    }
    return 0;
}

就近匹配示例

  • throw 抛出异常对象后,会生成异常对象的拷贝,因为抛出的异常对象可能是临时对象。这个拷贝的临时异常对象会在被 catch 后销毁(类似于函数的传值返回)。
  • catch(...) 可以捕获任意类型的异常对象,但不知道具体的异常错误。
int main() {
    try {
        throw runtime_error("Runtime exception occurred!");
    } catch (...) {
        cout << "Unknown exception!" << endl;
    }
    return 0;
}

通配符捕获示例

  • 实际上,抛出的异常对象类型实际上也不需要完全匹配,比如:可以抛出派生类对象用基类捕获。

函数调用链中的匹配原则

  1. 当异常在 try 代码块中 throw 抛出时,它会沿函数调用链向上传播,直到找到匹配的 catch 代码块而且此过程中,throw 后面的代码不再执行。
  2. 匹配到对应异常对象的类型的 catch 代码块后,沿着函数调用链销毁沿途的对象。
  3. 如果最后在 main 函数中没有匹配的 catch,程序会调用 terminate 函数,通常导致程序终止。

一般为了避免这种情况,需要用 catch(...) 防止异常没有捕获导致程序崩溃。

调用链传播示例

具体例子:

void testException(int x) {
    if (x == 0) throw "Division by zero error!"; // 抛出 const char* 类型的异常
    cout << "x:" << x << endl;
}

void test1() {
    try {
        testException(0);
    } catch (const string& e) {
        cout << "(string) Caught an exception: " << e << endl;
    }
}

int main() {
    try {
        test1();
    } catch (const char* e) {
        cout << "Caught an exception: " << e << endl;
    }
    catch (...) // 如果抛出的异常未经处理,程序会被 terminate 终止
    {
        cout << "Unknown exception!" << endl;
    }
    return 0;
}

未捕获终止示例

异常的重新抛出

在 catch 代码块中,可以通过 throw 关键字将异常重新抛出,供更高层次的代码处理。

void testException(int x) {
    if (x == 0) throw "Division by zero error!"; // 抛出 const char* 类型的异常
    cout << "x:" << x << endl;
}

void test1() {
    try {
        testException(0);
    } catch (const char* e) {
        cout << "(Inner catch) Caught an exception: " << e << endl;
        // 将捕获到的异常重新抛出
        throw;
    }
}

int main() {
    try {
        test1();
    } catch (const char* e) {
        cout << "(Outer catch) Caught an exception: " << e << endl;
    }
    catch (...) // 如果抛出的异常未经处理,程序会被 terminate 终止
    {
        cout << "Unknown exception!" << endl;
    }
    return 0;
}

重新抛出示例

异常安全

  • 构造函数最好不要抛异常,因为可能导致对象不完整初始化甚至完全没有初始化。
  • 析构函数也最好不要抛异常,否则可能造成资源泄漏。
void testException(int x) {
    int* array = new int[10];
    if (x == 0) throw "Division by zero error!"; // 抛出 const char* 类型的异常
    cout << "x:" << x << endl;
    delete[] array;
}

int main() {
    try {
        testException(0);
    } catch (const char* e) {
        cout << "Caught an exception: " << e << endl;
    }
    return 0;
}

这里由于 throw 抛异常提前退出了 testException 函数导致执行 delete[] array 导致了资源泄漏,这是非常危险的,一定要避免。

异常规范

  • 在函数的后面接 throw(type),表示这个函数可能抛出的所有的异常类型。
  • 函数后面接 throw() 表示函数不抛异常,在 C++11 中新增关键字 noexcept,表示该函数不抛异常。
  • noexcept 会影响异常的捕获,确认函数不会加才使用。
// 这里表示这个函数会抛出 A/B/C/D 中的某种类型的异常
void fun() throw(A, B, C, D);

// 这里表示这个函数只会抛出 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 中新增的 noexcept,表示不会抛异常
thread() noexcept;
thread(thread&& x) noexcept;

C++ 标准库异常

C++ 标准库中提供了一组异常类,用于支持异常处理机制。这些异常类都继承自 exception,提供了统一的接口以便捕获和处理各种类型的异常。

核心异常类:
exception 是描述标准库异常的基类,其接口为 virtual const char* what() const noexcept,返回异常的描述信息。

  • bad_alloc:表示内存分配失败的异常,通常由 new 操作符抛出。
  • bad_cast:表示动态类型转换(dynamic_cast)失败的异常。
  • bad_typeid:在对空指针调用 typeid 时抛出。
  • bad_exception:如果异常对象在 throw 时不匹配声明的异常类型,可能会抛出此异常。

逻辑错误异常:
logic_error 是一个逻辑错误的基类,表示程序中的逻辑问题,通常在运行时能够检测到。

  • domain_error:表示函数参数超出定义域的异常,比如尝试对负数求平方根。
  • invalid_argument:表示无效参数引发的异常,比如传递非法格式的字符串。
  • length_error:表示试图创建超出容器最大长度的对象,比如向 vector 添加过多元素。
  • out_of_range:表示访问容器中不存在的元素时抛出的异常,比如使用越界的索引访问 vector。

运行时错误异常:
runtime_error 是运行时错误的基类,表示在程序运行过程中发生的错误。

  • range_error:表示计算结果超出表示范围的异常。
  • overflow_error:表示算术运算发生上溢的异常。
  • underflow_error:表示算术运算发生下溢的异常。

其他异常:

  • ios_base::failure:表示与输入/输出流相关的错误,比如文件读取失败。

目录

  1. C 语言处理错误
  2. C++ 异常
  3. 异常的抛出与捕获
  4. 基本语法
  5. catch 的匹配原则
  6. 函数调用链中的匹配原则
  7. 异常的重新抛出
  8. 异常安全
  9. 异常规范
  10. C++ 标准库异常

更多推荐文章

查看全部
  • 大模型应用开发入门:从原理到 LangChain 实战
  • AIGC 时代的核心协议:深入理解 Model Context Protocol (MCP)
  • YOLO12 目标检测 WebUI 快速部署实战
  • 2024 年人工智能治理蓝皮书发布:全球治理进入规则构建关键阶段
  • GitHub Copilot Workspace 100 万 Token 上下文:价值与局限分析
  • 常用 AI Agents 解析:大模型行业应用入门
  • 基于大模型的聊天助手构建案例与优化实践
  • C 语言实现面向对象编程的核心思想与示例
  • 从零搭建 AI 系统权限控制系统
  • Java 分治算法实战:快速排序与归并排序
  • Java 模拟算法实战:LeetCode 经典题解与思路
  • 企业投融资模式与法律风险分析
  • Python 爬虫实战:爬取酷狗音乐热门歌曲榜单
  • 数据库 SQL 防火墙:内核级防护与 SQL 注入防御方案
  • 前端国际化最佳实践指南
  • KingbaseES 内核级 SQL 防火墙白名单防护机制与性能测试
  • RAGFlow 开源知识库部署与实战详解
  • OpenClaw 多端交互实测指南:Web、TUI 与钉钉集成
  • OpenClaw 大龙虾机器人安装与配置教程
  • 无人机航测正射影像制作:ContextCapture 与 Pix4D 实战指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online