百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

现代 C++ 异常和错误处理的最佳做法

lipiwang 2024-12-01 00:49 8 浏览 0 评论

在现代 c + + 中,在大多数情况下,报告和处理逻辑错误和运行时错误的首选方法是使用异常。 当堆栈中可能包含检测到错误的函数之间的多个函数调用,以及具有用于处理错误的上下文的函数时,尤其如此。 异常为检测到错误的代码提供了一种明确定义的方法,以便在调用堆栈上传递信息。

异常代码使用异常

程序错误通常分为两类:由编程错误引起的逻辑错误,例如,"索引超出范围" 错误。 并且是超出程序员控件的运行时错误,例如,"网络服务不可用" 错误。 在 C 样式编程和 COM 中,通过返回一个表示错误代码或特定函数的状态代码的值,或者通过设置调用方在每次调用函数后可以选择检索的全局变量来查看是否报告了错误,从而管理错误报告。 例如,COM 编程使用 HRESULT 返回值将错误传递给调用方。 和 Win32 API 包含 GetLastError 用于检索调用堆栈报告的最后一个错误的函数。 在这两种情况下,由调用方来识别代码并对其进行适当的响应。 如果调用方未显式处理错误代码,则程序可能会崩溃,而不发出警告。 或者,它可能会使用错误的数据继续执行,并生成不正确的结果。

在现代 c + + 中首选例外,原因如下:

  • 异常会强制调用代码识别错误情况并对其进行处理。 未经处理的异常停止程序执行。
  • 异常跳转到调用堆栈中可处理错误的点。 中间函数可以让异常传播。 它们不必与其他层进行协调。
  • 异常堆栈展开机制会在引发异常后根据定义完善的规则破坏范围内的所有对象。
  • 异常使检测到错误的代码和处理错误的代码之间的完全分离。

以下简化的示例显示了在 c + + 中引发和捕获异常的必要语法。

Bash
#include <stdexcept>
#include <limits>
#include <iostream>

using namespace std;

void MyFunc(int c)
{
    if (c > numeric_limits< char> ::max())
        throw invalid_argument("MyFunc argument too large.");
    //...
}

int main()
{
    try
    {
        MyFunc(256); //cause an exception to throw
    }

    catch (invalid_argument& e)
    {
        cerr << e.what() << endl;
        return -1;
    }
    //...
    return 0;
}

C + + 中的异常类似于 c # 和 Java 等语言。 在 try 块中,如果引发异常,则它将被caught catch 其类型与异常匹配的第一个关联块捕获。 换言之,执行从语句跳转 throwcatch 语句。 如果未找到可用的 catch 块, std::terminate 则将调用并退出程序。 在 c + + 中,可能会引发任何类型;但是,我们建议你引发直接或间接从派生的类型 std::exception 。 在上面的示例中,异常类型在 invalid_argument 标头文件的标准库中定义 <stdexcept> 。 C + + 不提供或不需要 finally 块,以确保在引发异常时释放所有资源。 资源获取是 (RAII) 使用智能指针的使用情况进行初始化,它为资源清理提供所需的功能。 有关详细信息,请参阅 如何:设计异常安全性。 有关 c + + 堆栈展开机制的信息,请参阅 异常和堆栈展开。

基本指导原则

强大的错误处理对于任何编程语言都很有挑战性。 尽管异常提供了多个支持良好错误处理的功能,但它们无法为你完成所有工作。 若要实现异常机制的优点,请在设计代码时记住异常。

  • 使用断言来检查绝不应发生的错误。 使用异常来检查可能出现的错误,例如,公共函数参数的输入验证中的错误。 有关详细信息,请参阅 异常与断言 部分。
  • 当处理错误的代码与通过一个或多个干预函数调用检测到错误的代码分离时,使用异常。 当处理错误的代码与检测到错误的代码紧密耦合时,考虑是否使用错误代码而不是在性能关键循环中。
  • 对于可能引发或传播异常的每个函数,请提供以下三种异常保证之一:强保障、基本保证或 nothrow (noexcept) 保证。 有关详细信息,请参阅 如何:设计异常安全性。
  • 按值引发异常,按引用来捕获异常。 不要捕获无法处理的内容。
  • 不要使用 c + + 11 中已弃用的异常规范。 有关详细信息,请参阅异常规范和 noexcept 部分。
  • 应用时使用标准库异常类型。 从 exception 类层次结构派生自定义异常类型。
  • 不允许对析构函数或内存释放函数进行转义。

异常和性能

如果没有引发异常,则异常机制的性能开销将最小。 如果引发了异常,堆栈遍历和展开的开销大致相当于函数调用的成本。 在输入块后,需要其他数据结构来跟踪调用堆栈 try ,并在引发异常时展开堆栈需要其他说明。 但在大多数情况下,性能和内存占用量的成本并不重要。 异常对性能的不利影响可能仅对内存约束的系统很重要。 或者,在性能关键循环中,错误可能会定期发生,并且代码与处理它的代码之间的紧密耦合。 在任何情况下,不可能知道异常的实际成本,而不会进行分析和度量。 即使在这种极少的情况下,成本很高,你也可以将其与更好的正确性、更易于维护性以及由设计良好的异常策略提供的其他优势进行权衡。

异常与断言

异常和断言是在程序中检测运行时错误的两种不同机制。 在 assert 开发过程中使用语句来测试条件,前提是所有代码都是正确的。 由于错误表明必须修复代码中的某些内容,因此不需要使用异常来处理此类错误。 它不表示程序必须在运行时恢复的条件。 assert在语句处停止执行,以便您可以检查调试器中的程序状态。 异常继续从第一个适当的 catch 处理程序执行。 使用异常检查在运行时可能发生的错误条件,即使代码正确,例如,"找不到文件" 或 "内存不足"。 异常可以处理这些情况,即使恢复只是将消息输出到日志并结束程序。 始终使用异常检查公共函数的自变量。 即使您的函数没有错误,也可能无法完全控制用户可能传递给它的参数。

C + + 异常与 Windows SEH 异常

C 和 c + + 程序可以在 Windows 操作系统中使用结构化异常处理 (SEH) 机制。 SEH 中的概念与 c + + 异常中的概念相似,不同之处在于 SEH 使用 __try__except__finally 构造而不是 trycatch 。 在 Microsoft c + + 编译器 (MSVC) 中,为 SEH 实现 c + + 异常。 但是,在编写 c + + 代码时,请使用 c + + 异常语法。

有关 SEH 的详细信息,请参阅 结构化异常处理 (C/c + +) 。

异常规范和 noexcept

异常规范是在 c + + 中引入的,它是一种指定函数可能引发的异常的方法。 不过,异常规范在实践中已证明有问题,并且已在 c + + 11 草案标准中弃用。 建议你不要使用 throw 除外的异常规范 throw() ,这表示函数不允许对任何异常进行转义。 如果你必须使用已弃用窗体的异常规范 throw( type-name ) ,则 MSVC 支持是有限的。 有关详细信息,请参阅 异常规范 (throw) 。 noexcept 说明符在 c + + 11 中引入为的首选替代项 throw() 。

相关推荐

软件测试|MySQL CROSS JOIN:交叉连接的详细解析

简介在MySQL数据库中,CROSSJOIN是一种用于生成两个或多个表的笛卡尔积的连接方法。CROSSJOIN不需要任何连接条件,它将左表的每一行与右表的每一行进行组合,从而生成一个包含所...

「MySQL笔记」left join-on-and 与 left join-on-where 的区别

1.摘要关于这两种写法的重要知识点摘要如下:left-join时,即使有相同的查询条件,二者的查询结果集也不同,原因是优先级导致的,on的优先级比where高on-and是进行韦恩运算连接...

MySQL中的JOIN——联合查询的基本语法

MySQL中的JOIN指令用来将两个或多个表中的数据进行联合查询,根据连接条件来匹配记录,从而得到需要的结果集。在MySQL中,常见的JOIN类型包括INNERJOIN、LEFTJOIN和RIGH...

MySQL 中的 CROSS JOIN:强大的连接工具

CROSSJOIN在MySQL里是一种挺特别的连接操作,它能弄出连接表的笛卡尔积。这就是说,要是表A有m行,表B有n行,那ACROSSJOINB的结果就会有m*n...

大厂必问:MySQL 三表 JOIN 操作的解析与性能优化,效率又如何?

大厂必问:MySQL三表JOIN操作的解析与性能优化策略,效率又如何?点击关注,开启技术之旅!大家好,这里是互联网技术学堂,无论你是一名程序员、设计师、还是对技术充满好奇心的普通人,都欢迎你加入...

面试题:MySQL 的 JOIN 查询优化(mysql查询优化方法)

MySQL的JOIN查询优化是提升数据库性能的关键环节。以下是综合多个技术文档的核心优化策略,按优先级和实现难度分类:一、索引优化:性能提升的基础为连接字段建立索引确保参与JOIN的列(通常...

Flink中处理维表关联技术实现路径

在Flink中处理维表关联大体氛围TableSQLLookupJoin和DataStream算子函数,主要技术实现路径:I.FlinkSQL/TableAPI中的Lookup...

深入剖析Zookeeper原理(一)整体设计

1.ZK集群架构设计与特性1.ZK集群架构设计:ZK主要分为三种角色:Leader(领导者):一个Zookeeper集群同一时间只会有一个实际工作的Leader,它会发起并维护与各Follwer及...

多种负载均衡算法及其Java代码实现

首先给大家介绍下什么是负载均衡负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。负载均衡,英...

一分钟了解SpringCloud中的ribbon到底是什么,原理是啥?

1.概念ribbon是一款客户端负载均衡器,用于微服务之间的负载均衡。首先,什么是客户端负载均衡?如图,ribbon可以通过注册中心获取服务列表,然后自己执行自己的负载均衡策略来决定要访问哪个微服务,...

Step by Step之腾讯云短信-验证码实践

在商城小程序和前端上线用了一阵子之后,用户提出了体验提升的需求,如忘记密码、绑定用户、快捷注册等,作为业界最佳实践的短信验证码登录、重置密码和注册等功能开发也就提上日程了,本文就以重置密码为例,将验证...

10分钟入门响应式:Springboot整合kafka实现reactive

Springboot引入Reactor已经有一段时间了,笔者潜伏在各种技术群里暗中观察发现,好像scala圈子的同仁们,似乎对响应式更热衷一点。也许是因为他们对fp理解的更深吧,所以领悟起来障碍性更少...

使用java随机生成有个性的用户名,LOL地名+水浒传,合计2808个

*随机生成用户名*取水浒传108好汉名字*取LOL地名26个,组合而成*一共可以生成2808个不同特色的用户名如果你在上网的时候,用户名难取的话,这里有很多可选择的用户名,现提供100个...

深入理解Math.random()的概率分布特性

直接上源码/***Returnsa{@codedouble}valuewithapositivesign,*返回一个带符号的double类型的数字,说人话就是返回一个非负...

编程英文 - 创建/生成/构建 (create/generate/build)

在软件开发中,create、generate和build这三个词经常被用到,它们都与"创造"或"产生"某些东西有关,但在具体使用场景和含义上有所不同。基本含义creat...

取消回复欢迎 发表评论: