【问题标题】:c++: Operator overloading and error handlingc++:运算符重载和错误处理
【发布时间】:2010-12-23 14:37:04
【问题描述】:

我目前开始研究 C++ 中的运算符重载,用于一个简单的 2D 顶点类,其中位置应该可以通过 [] 运算符获得。这通常有效,但我真的不知道如何处理错误,例如如果运算符超出范围(在只有 x 和 y 值的 2D 顶点类的情况下,如果它大于一)

在这种情况下处理错误的常用方法是什么?

谢谢

【问题讨论】:

  • 这就是我现在所做的:class ArrayOutOfBounds: public exception{ virtual const char* what() const throw() { return "The Array you are out of bounds!"; } }; T& operator[](uint32 _i){ try{ if(_i>1){ throw exOutOfBounds; } 其他 { 返回位置 [_i]; } } catch (exception& e){ cout

标签: c++ error-handling operator-overloading


【解决方案1】:

当你必须throw时,你必须throw。除非您可以返回某种神奇的爆炸结果值,否则没有其他方法可以诊断重载运算符中的问题。定义一个异常类型,抛出错误,记录它。

【讨论】:

    【解决方案2】:

    在最好的情况下,错误处理是一个棘手的问题。这几乎可以归结为错误有多大,以及当它发生时如果预期会发生什么。

    您可以遵循四种基本路径:

    1. 抛出异常
      • 错误处理的大锤。一个很棒的工具,如果你需要它肯定想使用它,但如果你不小心,你最终会砸到自己的脚。
      • 基本上跳过了throwcatch 之间的所有内容,只留下死亡和毁灭。
      • 如果没有被捕获,它将中止您的程序。
    2. 返回一个表示失败的值
      • 让程序员检查是否成功并做出相应反应。
      • 失败值取决于类型。指针可以返回NULL0,STL 容器返回object.end(),否则可以使用未使用的值(例如-1"")。
    3. 优雅地处理条件
      • 有时,错误并不是真正的错误,只是一种不便。
      • 如果仍然可以提供有用的结果,那么错误很容易被扫到地毯下而不会伤害任何人。
      • 例如,超出范围的错误可以只返回数组中的最后一个变量,而无需求助于任何混乱的异常。
      • 只要它是可预测的定义的,程序员就可以按照他们的意愿进行操作。
    4. 未定义的行为
      • 嘿,程序员一开始就不应该给你错误的输入。让他们受苦。

    一般来说,我只会针对那些破坏程序的东西,对于那些我真的不希望在不齐心协力的情况下恢复的东西,才使用选项一。否则,使用异常作为一种流控制形式比回到goto 的日子好不了多少。

    选项二可能是最常见的非程序破坏错误,但它的有效性实际上取决于您正在处理的返回类型。这是有利的,因为它允许程序员通过检测故障并自行恢复来在本地控制流程。在处理重载运算符时,它的用途有限,但为了完整起见,我想我会把它扔进去。

    选项三是针对具体情况的。许多错误无法以这种方式处理,甚至可能导致不直观结果的错误。请谨慎使用,并确保彻底记录。或者,根本不记录它,假装它是选项四。


    现在,至于所提供的具体示例,作为重载operator[] 的超出范围错误,我个人会选择选项四。不是因为我特别喜欢看到其他程序员在处理我的代码时受苦(顺便说一句,我确实喜欢,但这与讨论无关),而是因为这是意料之中的。

    大多数情况下,程序员会使用operator[],他们希望自己处理边界检查,而不是依赖类型或类来为他们做任何事情。即使在 STL 容器中,您也可以看到 operator[](无范围检查)与其他多余的 object.at() 进行 范围检查)并行。使用您自己的重载运算符反映预期行为往往会使代码更直观。

    【讨论】:

    • 好的,谢谢,我会选择选项四!不管怎样,你的帖子真的很有帮助!
    • “否则,使用异常作为流控制的一种形式,比回到 goto 时代好不了多少。”哎呀,异常首先是为了解决错误检查的问题。他们确保您的代码正确处理错误。未能捕获异常将终止程序,当您的程序忽略处理错误时,这是​​正确的做法(即使只是忽略错误,语言也无法知道它是否严重)。您首选的解决方案 2 的缺点是错误检查很容易被遗忘或忽略,从而使您的逻辑中断。
    【解决方案3】:

    根据 C++ 语言常见问题解答,operator[] 不应用于矩阵或二维数组实现;而是使用operator().Click here for FAQ #13.10

    最大的问题是为多个维度实现 []。

    至于错误,如果您不想为重载运算符提供任何额外参数(使用operator() 的另一个原因),则必须转到 exception 路由。

    【讨论】:

    • 我认为他的意思是一个有 2 个元素的向量(即 2D 空间中的一个点),而不是一个 2D 矩阵。
    • 没错!谢谢,我会调查的。
    【解决方案4】:

    我认为断言也可能存在。您是否预见到在二维向量中超出范围只会是(简单?)程序员的错误?

    T& operator[](size_t index)
    {
        assert(index < 2 && "Vector index out of bounds");
        return pos[index];
    }
    

    如果你要抛出异常,我想你也可以使用out_of_range - 或从它派生的类型。

    【讨论】:

      【解决方案5】:

      正如其他人所指出的,例外是要走的路。

      但是对于访问这样的点类来说,这似乎是一个非常不寻常的习惯用法。我会发现顶点类拥有单独的成员要简单得多:

      class Vertex {
          ...
          double x;
          double y;
      };
      

      然后你可以通过像vertex1.x - vertex2.x 这样的操作对它们进行操作,IMO 比vertex1[0] - vertex2[0] 更具可读性。为了额外的好处,它完全避免了您的异常问题。

      【讨论】:

      • 好吧,我希望它们都可以访问。你可以做 vertex1.x()-vertex2.x() 或 vertex[0]-vertex2[0]。基本上我想将数据放在一个数组中并添加不同的访问方式
      【解决方案6】:

      除了处理越界索引的异常之外,您至少有两个选项:

      • 只需相信您的输入,记录使用越界索引是未定义的行为,并让您的调用者成为专业人士[*]。
      • 如果索引超出范围,则通过直接调用 std::terminate()abort() 或其他方式(可能在打印错误消息后)中止。

      两者之间有一个折衷方案,即使用assert 宏。这将在发布版本中执行前者(使用 NDEBUG 编译),而在调试版本中执行后者。

      并不是说例外一定是个坏主意,但它们有自己的问题。再说一次,如果你没有发现这些问题,大部分问题都会消失。

      在这种情况下,调用者必须将 0 或 1 传递给您。如果他们有时将 2 传递给您,并计划捕获他们这样做时发生的异常,那么他们可能没有希望。不要花太多时间担心它。

      另一种选择是接受所有输入,但将它们映射到一个或其他值。例如,您可以按位和输入 1。这使您的代码非常简单,但明显的缺点是它掩盖了其他人的错误。

      [*] 并不是说​​专业人士不会犯错误。他们是这样。他们只是不希望你能把他们从错误中拯救出来。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-07-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多