代码之家  ›  专栏  ›  技术社区  ›  Steve

返回代码中异常的C++参数

  •  28
  • Steve  · 技术社区  · 15 年前

    我正在讨论一个新的C++项目中该走哪条路。我赞成例外而非返回代码(仅适用于例外情况),原因如下-

    1. 构造函数不能提供返回代码
    2. 将故障路径(应该非常少)与更干净的逻辑代码分离
    3. 在非特殊情况下更快(不检查是否/其他成百上千次)
    4. 如果有人弄错了返回代码设置(忘记返回失败),可能需要很长时间才能找到。
    5. 从错误中包含的消息中获得更好的信息。(有人向我指出,返回枚举对错误代码也可以这样做)
    6. 从Jared Par 没有专门设计用来处理它的代码是不可能忽略的

    这些是我从思考和谷歌搜索中得出的观点。我必须承认,在过去的几年里,我一直倾向于在C工作的例外情况。请说明在返回代码上使用异常的进一步原因。对于那些喜欢返回代码的人,我也愿意倾听你的推理。谢谢

    11 回复  |  直到 7 年前
        1
  •  44
  •   Nikola Smiljanić    15 年前

    我想这个 article 总结一下。

    使用异常的参数

    1. 异常将错误处理代码与正常程序流分开,从而使代码更可读、更健壮和可扩展。
    2. 抛出异常是从构造函数报告错误的唯一干净方法。
    3. 与错误代码不同,异常很难被忽略。
    4. 异常很容易从深度嵌套的函数传播。
    5. 异常可以是(通常是)用户定义的类型,这些类型比错误代码携带更多的信息。
    6. 异常对象通过使用类型系统与处理程序匹配。

    反对使用异常的论据

    1. 异常通过创建多个不可见的出口点来破坏代码结构,这些出口点使代码难以读取和检查。
    2. 异常很容易导致资源泄漏,特别是在没有内置垃圾收集器和最终块的语言中。
    3. 学习编写异常安全代码是困难的。
    4. 例外是昂贵的,违反了只为我们使用的东西付费的承诺。
    5. 异常很难引入到遗留代码中。
    6. 在执行属于正常程序流的任务时,异常很容易被滥用。
        2
  •  10
  •   Greg D    15 年前

    我听过的最好的选择是返回代码而不是异常:

    1. 编写异常安全代码是 坚硬的 [在C++中]。

    在我自己最近的大量经验中,我可以同情你使用异常的愿望,但不幸的是C++不是C语言,很多在C++中可以逃脱的东西在C++中可能是致命的。

    一个很好的总结 the case for and against 可以在 Google's style guidelines . 简而言之:

    赞成的意见:

    • 异常允许更高级别的应用程序决定如何 处理“无法发生”的故障 深度嵌套函数,没有 模糊和容易出错的簿记 错误代码的。
    • 大多数现代语言都使用例外。使用它们 C++会使它更符合 Python、Java和C++ 熟悉。
    • 一些第三方C++库使用异常,并将它们关闭。 内部原因使得 与这些库集成。
    • 异常是构造函数失败的唯一方法。我们可以模拟 具有工厂功能或 init()方法,但这些方法需要堆 分配或新的“无效”状态, 分别。
    • 异常在测试框架中非常方便。

    欺骗:

    • 向现有函数添加throw语句时,必须 检查它的所有可传递调用方。 要么他们必须至少 基本例外安全保证,或 他们绝不能抓住例外 对这个项目感到满意 因此终止。例如, 如果f()调用g()调用h()和h 抛出一个F捕获的异常,g 必须小心,否则它可能不干净 正常工作。
    • 一般来说,异常会使程序的控制流变得困难。 通过查看代码进行评估: 功能可能会在您所在的位置返回 不要期望。这一结果 可维护性和调试 困难。你可以最小化这个 通过一些关于如何和在哪里的规则来确定成本 可以使用异常,但在 开发人员需要的更多成本 了解和理解。
    • 例外安全要求RAII和不同的编码实践。 需要大量的配套机械 确保正确编写异常的安全性 代码容易。此外,为了避免 读者理解整个电话 图形,异常安全代码必须 隔离写入的逻辑 持续状态变为“提交” 相位。这将有两个好处 成本(可能是你被迫的地方) 模糊代码以隔离 承诺)。允许例外会 迫使我们始终支付这些费用 即使他们不值得。
    • 打开异常会向生成的每个二进制文件添加数据,增加 编译时间(可能稍长)和 可能会增加地址空间 压力。
    • 异常的可用性可能会鼓励开发人员抛出异常 当它们不合适或 在不安全的时候从他们身上恢复过来 这样做。例如,无效用户 输入不应导致异常 被扔掉。我们需要 样式指南的文档长度更长 这些限制!

    我建议通读并理解优缺点,然后基于这些为您自己的项目做一个决定。你没有和谷歌一样的软件,所以对他们有意义的可能对你没有意义(这就是为什么我忽略了他们的结论)。

        3
  •  6
  •   JaredPar    15 年前

    imho,1选择异常而不是返回代码的原因是您不能静默地忽略异常。这样做至少需要最少的额外代码。

        4
  •  4
  •   Raedwald    7 年前

    在异常错误条件下使用异常。你有一些很好的论据,我想反驳一些论据。

    首先,标准C++库本身使用异常,到处都是。如果没有容器类或iostreams,就不能使用它们。因为很多有用的特性都会用到它们,所以试图在没有它们的情况下相处会带来很多问题。

    其次,一旦学会了如何编写异常安全代码并不难。它需要始终如一的RAII,但无论如何你都应该这样写。您应该采用构造提交方法,但这通常是一种优势,并且可以避免一些细微的错误。(例如,使用复制交换方法可以完全消除自分配问题。)根据我的经验,异常安全代码在总体上看起来更好。这是C++程序员必须学习的,但是C++程序员必须学习很多东西,而这并不是那么多。

    第三,如果你限制例外情况,应该 minimal effects on performance . 正如Pavel Minaev所指出的,如果必须将错误代码传递给结果,那么就有可能对性能产生影响,因为C++没有设置多个值的简单返回。

    第四,很难保证旧代码异常的安全。然而,这是一个新项目。

    所以,我没有看到在特殊情况下不抛出异常的好理由,而且有很多理由这样做。

        5
  •  3
  •   Anon.    15 年前

    在非特殊情况下更快(不检查是否/其他成百上千次)

    在非异常情况下,只需进行一次比较,即可确定是否返回了e_success。

    如果有人弄错了返回代码设置(忘记返回失败),可能需要很长时间才能找到。

    如果有人没有检查异常,那么在您实际收到异常抛出之前很难注意到。如果你处理的是错误代码,你只需看看它们是否在检查它们就知道了。

        6
  •  3
  •   Stack Overflow is garbage    15 年前

    用有意义的东西。我想两者都有一席之地。在某些情况下,错误代码几乎不可能使用(例如,从构造函数返回故障)

    其他时候,错误代码只是更方便而已。如果你希望他们发生,他们更容易处理。例外适用于 例外 错误——那些本不应该发生的错误,但在蓝月里可能会发生一次。对于预期会定期发生并可在本地处理的错误,错误代码更加方便。异常在必须在调用堆栈上进一步处理错误的情况下最有用。

    此外,在非异常情况下,异常并不一定更快。通常,它们需要函数prolog和epilogs中的额外异常处理代码,每次调用函数时都必须执行这些代码,不管函数是否引发异常。

        7
  •  3
  •   Liz Albin    15 年前

    一般来说。当恢复是可能的和预期的,然后使用返回代码。

    当无法恢复时 或不需要 然后使用异常。

    错误处理是困难的,编写干净的代码,不管有没有异常都是困难的。

    由于这是一个新项目,您不必担心旧代码异常的安全性,但是您确实需要担心编写清晰的代码。

    在适当的情况下,通过使用异常来实现这一点。

        8
  •  3
  •   Ed Swangren    15 年前

    由于许多其他人已经提供了在错误代码上使用异常的技术原因,我将给出一个实际的原因。

    我在一个复杂的系统上工作,它使用返回代码而不是异常。现在,这是设计得很好的代码,但我敢打赌,平均而言,每个函数中大约70%的代码是错误处理代码。典型的函数如下所示:

    long Foo( )
    {
        long retCode = MoveStage( );
        if ( retCode != RetCode_OK )
        {
            logger->LogError( __LINE__, __FILE__, "some message" );
            return( retCode );
        }
    
        int someConfigVar = 0;
        long retCode = GetConfigVar( "SomeVarName", someConfigVar );
        if ( retCode != RetCode_OK )
        {
            logger->LogError( __LINE__, __FILE__, "some message" );
            return( retCode );
        }
    
        long retCode = DoSomething( );
        if ( retCode != RetCode_OK )
        {
            logger->LogError( __LINE__, __FILE__, "some message" );
            return( retCode );
        }
    
        // and on and on and on...
    }
    

    代码中充满了这些,很难理解。除此之外,在许多地方返回代码被完全忽略,因为我们知道调用不会失败。每个函数都返回一个ret代码,所以通常作为函数输出返回的内容必须作为out参数返回。而且,所有这些函数都只是在出错时返回retcode,所以我们只是在发生错误时将retcode冒泡到顶部。你不能这样集中你的错误处理策略,它会成为一个混乱的头疼问题。

        9
  •  2
  •   Chris Becke    15 年前

    编写异常安全代码是非常困难的。一个完全人为的例子是:

    void Class::Method()
    { 
      i++;
      SomeCallThatMightThrow();
      j++;
    }
    

    将i++和j++替换为必须保持同步的任意两个变量resources状态。垃圾收集避免了我们必须记住将新的和删除的进行配对。具有讽刺意味的是,老式的显式返回代码测试避免了我们必须仔细分析可能抛出异常的每个函数,以检查它们是否与post条件相冲突。

        10
  •  1
  •   Doug T.    15 年前

    我喜欢C++的一件事是,很容易想到如何用C特性(更容易理解汇编)来实现更高级的功能。C++的异常打破了这个模型。要达到这样的理解水平,我必须做很多事情。刚读 this 在你理解它之前,你会花很多时间挠你的头。

    此外,异常要求您有良好的纪律,使您的代码异常安全,资源无泄漏。这意味着要将raii用于任何拥有资源的东西。

    此外,当我将异常测量为比简单的返回代码慢很多个数量级时,已经显示了异常。

    好吧,然后他们说你应该只在特殊情况下扔,但是你如何沟通那些非特殊的,预期的,经常发生的错误。当然是返回代码!:)

    我不知道这些好处值多少钱。

        11
  •  -4
  •   Charles Eli Cheese    15 年前
    1. 构造函数不能提供返回代码 (除非您可能在构造函数中抛出异常,否则您将面临一个受伤的世界)

    2. 将故障路径(应该非常少)与更干净的逻辑代码分离 (开辟了一条更广阔的失败之路。C++异常根本不像C语言。我喜欢例外。C++异常比无用更糟。部分原因是由于实现,部分原因是C++是什么而不是什么

    3. 在非特殊情况下更快(不检查是否/其他成百上千次) (不是真的。你必须检查相同数量的错误,不管发生什么,你只是不知道它们在哪里被检查。此外,还有一些重要的错误和一些不重要的错误,或者至少对您的代码不重要,所以您不必检查每一件事情(是否检查新的错误?)
    4. 如果有人弄错了返回代码设置(忘记返回失败),可能需要很长时间才能找到。 (我将对C++异常中的任何一个常见错误进行一百万个这样的错误)
    5. 从错误中包含的消息中获得更好的信息。(有人向我指出,返回枚举对错误代码也可以这样做) (那么,如何实现错误处理?这由你决定,是吗?您可以有一些异常,这些异常不会给出有用的信息或错误代码,它们会在失败时注销现有数据。在C++中,它们似乎永远不会在你真正想要的情况下提供有用的信息。
    6. 从jared par不可能忽视没有代码到专门设计来处理它 (当然,但这个代码通常是无用的代码。这就像通过一项法律,说每个人都必须有无缺陷的程序。不管怎样,不遵守这些法律的人,以及那些已经在应该处理错误的地方处理错误的人)

    至于反对的理由:

    1. 希望你的程序至少在某段时间能真正运行。
    2. 性能。
    3. 意识形态。如果这是一个错误,我们不应该从中“恢复”。我们要么死在错误消息中,记录并继续,要么完全忽略它。如果我们可以修复它,这不是一个错误,而是一个特性……如果您有意使用这样的代码,那么世界上没有任何调试工具可以拯救您。

    主要是一个。