【问题标题】:How do you keep track of exception safety guarantees offered by each function您如何跟踪每个功能提供的异常安全保证
【发布时间】:2011-10-26 12:59:39
【问题描述】:

在编写异常安全代码时,需要考虑所有调用函数的异常安全保证(none、basic、strong 或 no-throw)。由于编译器没有提供任何帮助,我认为函数命名约定在这里可能会有所帮助。是否有任何一种既定的符号标准来指示功能提供的异常安全保证水平?我在想一些类似匈牙利的东西:

void setFooB(Foo const& s); // B, offers basic guarantee
int computeSomethingS();    // S, offers strong guarantee
int getDataNT() throws();   // NT, offers no-throw
void allBetsAreOffN();      // N, offers no guarantee

编辑:我同意 cmets 认为这种命名约定很丑陋,所以请允许我详细说明我提出建议的原因。

假设我重构了一些代码,并在该过程中更改了函数提供的异常安全级别。如果保证已经从强到基本(也许通过提高速度来证明),那么每个调用重构函数的函数都必须重新考虑它们的异常安全性。如果保证的更改也触发了函数名称的更改,它将允许编译器帮助我至少标记更改函数的所有用途。这是我建议上述命名约定的理由,尽管它是有问题的。这与 const 非常相似,其中函数的 const 特性的变化会对其他调用函数产生级联影响,但在这种情况下,编译器会提供非常有效的帮助。

所以我想我的问题是,人们养成了什么样的工作习惯来确保代码真正满足他们预期的异常保证,尤其是在代码维护和重构期间。

【问题讨论】:

  • 拜托,不需要另一个代码疣系统,我们还不够吗?
  • “异常安全代码”是什么意思?没有必要确保永远不会抛出任何函数,并且所有标准容器都可能习惯性地抛出......通常你会确定程序流中可以明智地处理异常并处理那些你可以处理的地方(重新抛出所有其他人),而不用担心每个人来自哪里 - 否则你只是回到 C 风格的错误处理......
  • 不要那样做,就像 Gene 建议的那样。也许您可以将它们放在单独的名称空间中,用一些模板或默认参数支持它们。最好将它们放在单独的标题/命名空间中并妥善记录。
  • 一切都应至少提供基本保证,因此您不必担心没有。记录(通过 cmets 或好的、有意义的名称)提供更强保证的事物;根据我的经验,这些并不常见。考虑使用throws()noexcept 来明确保证不投掷(但也要研究throws() 的缺点)。请不要为此目的使用可怕的名称装饰。
  • @Kerrek SB:异常保证不是关于停止异常,而是关于在抛出异常时您对对象的保证。大多数对象可能应该提供强有力的保证(如 STL 容器)(它可以工作或抛出异常而不改变对象),但有时您需要的只是基本保证(对象不会处于无效状态) .很少有方法需要不抛出保证(除了析构函数和 swap()),你应该足够聪明,永远不要编写不提供保证的代码。

标签: c++ exception hungarian-notation


【解决方案1】:

我正在考虑添加

@par Exception Safety

Strong guarantee

在适当的情况下发送到我的 javadocs。

【讨论】:

    【解决方案2】:

    我理解你愿意做好,但我不确定这样的命名约定。

    总的来说,我对语言强制执行的命名约定保持警惕:它们很容易成为最大的骗子。

    如果您真的需要这些东西,我的建议是使用编译器(例如 Clang)并添加一组新属性。请注意,您需要编辑您的标准库提供的标头以及您依赖的所有第 3 方标头,以对它们进行注释,以便您可以从头开始获得这些保证。

    然后你可以让编译器检查注解(也不会是微不足道的......),并且然后注解变得有用,因为它们不能撒谎

    【讨论】:

      【解决方案3】:

      我认为你不需要做任何特别的事情。

      我真正记录的唯一一个是 no-throw,那是因为语言的语法允许它。

      您的项目中不应有不提供任何保证的代码。所以这只留下强大/基本的文件。对于这些,我认为您不需要显式调用它,因为它实际上不是关于方法本身,而是关于整个类(对于这两个保证)。他们提供的保证实际上取决于使用情况。

      我想我认为我对所有事情都提供了强有力的保证(但我没有)有时它太贵了有时它只是不值得付出努力(如果东西要扔了,它们无论如何都会被摧毁)。

      【讨论】:

        【解决方案4】:

        我通常发现自己在 cmets (doxygen) 中记录了这一点,除了 no throw 保证,我经常使用 throw() 异常规范 标记当且仅当确保函数保证不会抛出,异常安全很重要。

        也就是说,我通常更担心代码中未处理的异常会导致问题的部分异常,并在本地进行处理(通过其他方式确保您的代码是异常安全的,例如 RAII,在外部执行工作并然后将结果与不抛出操作合并——即不抛出交换,这是我主动标记为 throw() 的唯一函数。

        其他人可能有其他经历,但我发现这对于我的日常工作来说已经足够了。

        【讨论】:

          猜你喜欢
          • 2010-09-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-24
          • 2015-04-25
          • 2021-05-14
          • 2018-11-19
          相关资源
          最近更新 更多