5

我有 2 个关于非抛出函数的问题:

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

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

这是一个例子:

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

如果里面的代码swap根本不会抛出,我还应该追加throw()吗?为什么?

4

2 回答 2

5

throw()(或noexcept在 C++11 中)之所以有用,有两个原因:

  1. 它允许编译器在优化方面更加积极。
  2. 它告诉函数用户他们可以在他们自己的非抛出函数中使用这个函数。

非抛出函数对于编写异常安全代码非常重要。例如,编写异常安全的常用方法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吗?如果没有,当他们尝试使用该指针时会发生崩溃。此外,astd::bad_alloc通常意味着您的内存已用完,您只是将问题推迟。

所以要小心:有些函数可以不抛出,但应该允许其他函数抛出。异常安全不是一件小事。

于 2012-08-07T08:49:43.133 回答
3

为什么要使函数不抛出?

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

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

您可以通过将所有可能抛出内部的代码放在一个 try 块中来使代码不抛出,并在不重新抛出的情况下处理任何异常。

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

}

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

于 2012-08-07T08:33:06.137 回答