关于非投掷函数的混淆

时间:2012-08-07 08:27:24

标签: c++ exception-specification

我有2个关于非投掷功能的问题:

  1. 为什么要使一个函数不抛出?

  2. 如何使函数不抛出?如果函数内部的代码实际上可能是throw,那么我是否还应该让它不被抛出?

  3. 以下是一个例子:

    void swap(Type t1, Type t2) throw()
    {
        //swap
    }
    

    如果swap中的代码完全没有投放,我还应该追加throw()吗?为什么呢?

2 个答案:

答案 0 :(得分:4)

throw()(或C ++ 11中的noexcept)有两个原因:

  1. 它允许编译器在其优化中更积极。
  2. 告诉函数用户他们可以在自己的非投掷函数中使用此函数。
  3. 非抛出函数对于编写异常安全代码非常重要。例如,编写异常安全operator=的常用方法是通过非抛出swap()函数。

    另一方面,其他异常规范是无用的,并且在当前标准中已被公正地弃用。它们与模板完全不能很好地混合,而且执行起来太昂贵。

    现在,如果您在可能实际抛出的函数中使用noexcept规范,则所有投注均已关闭。即使您的编译器在异常离开函数时没有终止程序(例如,由于运行时效率原因,VS不执行此操作),您的代码可能由于优化而无法执行您的操作。例如:

    void f() noexcept
    {
      a();
      b();
    }
    

    如果a()实际抛出而b()有副作用,则函数行为将无法预测,因为您的编译器可能决定在b()之前执行a(),因为您有告诉它不会抛出异常。

    编辑:现在问题的第二部分:如何使函数不抛出?

    首先,您需要问问自己,您的功能是否真的应该是非投掷的。例如:

    class C
    {
      C* CreateInstance()
      {
        return new C();
      }
    }
    

    由于运算符new可以抛出std::bad_allocCreateInstance()可以抛出。您可以尝试使用try-catch块来避免它,处理或吞咽可能在try块内抛出的任何异常,但这真的是明智的吗?例如:

    C* CreateInstance()
    {
      try
      {
         return new C();
      }
      catch (...)
      {
         return null;
      }
    }
    

    似乎问题已经解决,但是您的来电者是否准备好CreateInstance()返回null?如果没有,当他们尝试使用该指针时会发生崩溃。此外,std::bad_alloc通常意味着你的内存耗尽而你只是推迟了问题。

    所以要小心:某些功能可以不投掷,但其他功能应该被允许抛出。例外安全不是一件小事。

答案 1 :(得分:2)

  

为什么要使一个函数不抛出?

因此函数的调用者可以调用它而无需采取任何操作来处理函数引发的异常。

  

如何使函数不抛出?如果函数内部的代码实际上可能会抛出,那么我还应该让它不抛出吗?

你可以通过放入所有可以抛出try块的代码来使代码不抛出,并处理任何异常而无需重新抛出。

void swap(T t1, T t2) noexcept {
  try {
    // ....
  } catch (const std::exception& ex) {
    // ....
  } catch (...) {
    // ....
  }

}

如果你声称你的功能没有扔,那么你必须让它不扔。但这并不意味着添加一个异常规范,你不应该这样做,因为它已被弃用。这意味着你必须确保功能不会抛出,无论如何。在这方面,C ++ 11关键字noexcept为您提供了一些编译时安全性。