【问题标题】:Should operators == or != throw in this case?运算符 == 或 != 在这种情况下应该抛出吗?
【发布时间】:2012-12-07 00:47:41
【问题描述】:

我正在编写一个程序,在其中创建了一个 POD 结构的 std::vector。结构的成员之一是唯一标识符。

为了能够使用 std::binary_search 我必须为结构实现 operatorhere 的指导方针,我正在为 ==、!=、、>= 和

这提出了一个我不知道如何处理的问题。向量将按我分配给每个结构的唯一 ID 排序。如果两个结构具有相同的标识符,则它们是相同的。但是,在我看来,如果两个结构具有相同的标识符但其他成员中的数据不同,则可能会出现这种情况。

这绝不应该发生。然后让比较运算符检查其余字段并在它们不同但ID相同时抛出异常是否合适?什么样的例外最合适?

【问题讨论】:

  • 我认为基于 id 比较的std::set 更适合这项任务。
  • 如果您希望此条件在运行时可恢复,那么throw 将是合适的。如果没有,assert 可能是更好的选择。
  • @chris 我选择了 std::vector 因为我需要内存是连续的。我不知道 set(或任何其他订购的容器)提供此保证。
  • 这里的比较是红鲱鱼。如果这些结构中的任何一个都不应该存在共享唯一标识符,那么为什么要等到比较它们才能发现呢?将此类型的构造函数设为私有,并提供保证它的工厂函数。
  • @GManNickG 存在相同结构的两个副本不是错误。错误是存在两个共享 ID 但信息不同的结构。如果我这样做了,结构将不再是 POD。

标签: c++ exception operator-overloading


【解决方案1】:

这只是SCombinator's answer.的扩展

你说过“这不应该发生”的事实。意味着您要使用断言,而不是异常(或两者的组合)。异常会隐藏错误 - 好吧,不是隐藏,但您将能够捕获它并继续。它更适合您没有真正计划的特殊情况 - 例如,您正试图打开一个不存在的文件。文件丢失并不是真正的逻辑的一部分,它是你的小弟弟不小心删除了你的文件,或者认为它是病毒的侵略性杀毒软件,或者其他 - 这只是一种特殊情况.

如果成员 ID 相同但其他方面不同是不应该发生的,这基本上是一个断言。这是逻辑的一部分——如果你愿意的话,它是要求的一部分。抛出异常只是指出了这一点,但实际上并没有一种方法可以从中恢复。当你意识到有些不对劲时,已经晚了 2 点。您有两个具有相同 id 但不同的对象,您不知道哪个是正确的,您不知道为什么存在不正确的对象,等等。你可能甚至不想从中恢复过来。应用程序已经处于错误状态 - 两个相互矛盾的对象已经存在。您的应用程序处于不可恢复的状态 - 如果您继续使用它,您可能会得到错误的结果或更糟。

如果不是关键断言,你也可以事后抛出异常,并提供一种干净的方式来关闭应用程序,但这只是一种美化。

一般来说,我遵循一个简单的规则 - 如果这是在特殊情况下可能发生的事情,我会使用例外。如果这是不应该发生的事情,并且如果确实发生了,则意味着代码中的逻辑存在严重错误,我会使用断言(并且可能会强制崩溃)。

【讨论】:

  • 谢谢。不过,为了澄清一点,如果 ID 相同且成员不同,则会发生错误,而不是相反。
  • @Kian 啊,好的。现在好多了? (虽然它不会改变推理):)
  • 是的,感谢您的解释。它确实更清楚地说明了何时使用它们。
【解决方案2】:

如果这样的事情是一个逻辑错误,你可能应该使用assert

【讨论】:

  • 在这里使用断言是正确的,但您可能想详细说明原因,以及为什么在这种情况下它比异常更好。
【解决方案3】:

好吧,你必须问自己几个问题:

1) 这是否可能在测试期间发生(即发出错误信号)但在正常执行期间绝不会发生?如果答案是肯定的,请使用assert

2) 这是否会在正常运行时发生,如果是,程序能否恢复并继续执行?如果答案是肯定的,则抛出错误,捕获并处理它。

3) 这是否会在正常运行时发生并且不可恢复?如果是,请致电 abort 或类似的名称 (exit, terminate) 或抛出您不会处理的异常。

【讨论】:

  • 如果只有 1) 是那么简单。您如何判断某些错误是否仅在测试期间发生而在正常执行期间从未发生?
  • 我猜,要么希望最好,要么完全停止使用断言。 (我的意思是使用完整的测试而不是断言。)这并不总是那么简单。但有时是可能的。
【解决方案4】:

创建数据时应拒绝不良数据。排除错误不是比较运算符的工作。这应该程序进行任何严重的数据操作之前完成。所以这个问题的答案是否定的,operator==operator!= 不应该对坏数据抛出异常;编写它们时应该假设传递给它们的数据是有效的。

【讨论】:

    【解决方案5】:

    您可以只使用通过二叉搜索树实现的map,尽管您会复制id。再说一次,您甚至不需要将 id 存储在结构中,因为它们应该是唯一的。

    // Example POD struct.
    struct MyStruct
    {
      int id; // Redundant.
      char a;     
    }
    
    std::map<int, MyStruct> myMap;
    MyStruct m; m.id = 1;
    myMap[m.id] = m;
    // Or simply..
    myMap[1] = m;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-17
      • 2022-07-22
      • 2018-06-16
      • 1970-01-01
      • 2013-05-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多