12

假设您有一个通常永远不会失败的函数,例如:

std::string convert_integer_to_string(int x);

原则上,这将是noexcept. 但是,实现很可能涉及动态内存管理,因此在使用运算符分配内存时,它总是会抛出std::bad_allocnew

是否建议将函数注释为 noexcept?

从实际的角度来看,以合理的方式处理内存不足的情况是极其困难的。大多数程序只是假设有足够的可用内存。std::terminate如果noexcept函数 throws会发生调用,std::bad_alloc在这种情况下似乎是合理的。

对我来说noexcept是某种形式的文档。这是您(或优化器)可以安全地假设此函数永远不会抛出的承诺。如果您正在编写一个不关心内存不足情况的应用程序,那么它仍然是一个有效的假设。

我想最安全的建议是永远不要使用noexcept如果std::bad_alloc可以抛出异常。另一方面,我想知道如果noexcept您不关心内存不足的情况(即,如果std::terminate可以的话),是否有任何优势可以使用。

4

2 回答 2

14

如果一个函数可以出于任何原因抛出异常,即使它是,也不std::bad_alloc应该将其声明为。真正不能抛出异常的函数相对较少,而且它实际上也很重要。函数的主要需求是允许在异常情况下检测可用的错误恢复选项:例如,在插入对象时可以使用移动构造,假设移动构造不会抛出。如果移动构造可以抛出,则在实现强异常安全操作时,移动对象不能用于恢复异常。因此,如果移动构造对于无法移动对象但需要复制它们的类型的实例化可能失败。noexceptnoexceptstd::vector<T, A>Tstd::vector<T, A>

特别是,不要用作虚假noexcept文档:如果函数实际上可以抛出,则属于违约行为。系统会在这种违规情况下对某种程度的定义行为做出反应,这一事实并不意味着您应该利用它。...虽然简单的程序可能不会恢复并在内存不足时死掉,但真正的程序可能至少需要存储足够的状态来恢复它们在死时留下的混乱,即任何函数都不能接受做出关于终止程序的决定(当然,除非这是记录在案的函数意图)。

于 2013-08-11T21:51:48.810 回答
3

我不确定我是否会担心内存不足异常。

在某些操作系统(至少是linux)下,内存不足时的默认行为将被操作系统(由oom 杀手)杀死。当您写入内存时(而不是分配内存时)会发生这种情况,并且您将没有机会运行任何清理代码。此功能称为内存过度使用

即使您确实获得了内存已用完的信息,也很难正确处理这些错误:您需要绝对确保您的异常处理程序不会分配内存。这包括您从该错误处理程序中获得的所有功能,您还需要确保可能在此过程中触发的任何通用异常处理程序(例如日志记录)不使用任何内存。您通常可以期望的最好的结果是在关闭程序之前进行一些简单的清理。

请注意,您也可以使用std::nothrow来检查分配结果而不使用异常(也就是说,提供您的操作系统实际上会在分配时告诉您该信息)。当您执行您认为可能会失败的大分配时,这样做可能是有意义的。这也有一个很好的属性,而不是处理(可能)未捕获的异常,您将获得一个相当容易调试的 nullptr。

于 2013-08-12T14:40:02.807 回答