我有 2 个关于非抛出函数的问题:
为什么要使函数不抛出?
如何使函数不抛出?如果函数内的代码实际上可能
throw
,那么我是否仍然应该让它不抛出?
这是一个例子:
void swap(Type t1, Type t2) throw()
{
//swap
}
如果里面的代码swap
根本不会抛出,我还应该追加throw()
吗?为什么?
我有 2 个关于非抛出函数的问题:
为什么要使函数不抛出?
如何使函数不抛出?如果函数内的代码实际上可能throw
,那么我是否仍然应该让它不抛出?
这是一个例子:
void swap(Type t1, Type t2) throw()
{
//swap
}
如果里面的代码swap
根本不会抛出,我还应该追加throw()
吗?为什么?
throw()
(或noexcept
在 C++11 中)之所以有用,有两个原因:
非抛出函数对于编写异常安全代码非常重要。例如,编写异常安全的常用方法operator=
是通过非抛出swap()
函数。
另一方面,其他异常规范是无用的,并且在当前标准中已被合理地弃用。它们根本不能与模板很好地混合,而且执行起来成本太高。
现在,如果您noexcept
在可能实际抛出的函数中使用规范,那么所有的赌注都没有了。即使您的编译器在异常离开函数时没有终止程序(例如,出于运行时效率的原因,VS 不会这样做),您的代码也可能由于优化而无法执行您的想法。例如:
void f() noexcept
{
a();
b();
}
如果a()
真的抛出并b()
有副作用,函数的行为将是不可预测的,因为你的编译器可能决定在b()
之前执行a()
,因为你已经告诉它不会抛出异常。
编辑:现在是你问题的第二部分:如何使函数不抛出?
首先,您需要问自己您的函数是否真的应该是非抛出的。例如:
class C
{
C* CreateInstance()
{
return new C();
}
}
由于操作员new
可以抛出一个std::bad_alloc
,CreateInstance()
所以可以抛出。您可以尝试使用 try-catch 块来避免它,处理或吞下任何可能在try
块内抛出的异常,但这真的明智吗?例如:
C* CreateInstance()
{
try
{
return new C();
}
catch (...)
{
return null;
}
}
问题似乎解决了,但是您的来电者会准备好CreateInstance()
返回null
吗?如果没有,当他们尝试使用该指针时会发生崩溃。此外,astd::bad_alloc
通常意味着您的内存已用完,您只是将问题推迟。
所以要小心:有些函数可以不抛出,但应该允许其他函数抛出。异常安全不是一件小事。
为什么要使函数不抛出?
因此,函数的调用者可以调用它,而无需采取任何措施来处理函数引发的异常。
如何使函数不抛出?如果函数内部的代码实际上可能会抛出,那么我还应该让它不抛出吗?
您可以通过将所有可能抛出内部的代码放在一个 try 块中来使代码不抛出,并在不重新抛出的情况下处理任何异常。
void swap(T t1, T t2) noexcept {
try {
// ....
} catch (const std::exception& ex) {
// ....
} catch (...) {
// ....
}
}
如果您声称您的函数不抛出,那么您必须使其不抛出。但这并不意味着添加异常规范,无论如何您都不应该这样做,因为它已被弃用。这意味着您必须确保该函数无论如何都不会抛出。C++11 关键字noexcept
在这方面为您提供了一些编译时安全性。