永远不要将涉及动态内存分配的函数注释为noexcept
Never annotate functions involving dynamic memory allocation as noexcept?
假设您有一个通常永远不会失败的函数,例如:
std::string convert_integer_to_string(int x);
从原则上讲,这将是noexcept
的候选者。然而,实现很可能涉及动态内存管理,因此在使用new
运算符分配内存时,它可能总是抛出std::bad_alloc。
是否建议将函数注释为noexcept
从实践的角度来看,以合理的方式处理记忆不足的情况是极其困难的。大多数程序只是假设有足够的内存可用。如果noexcept
函数抛出std::bad_alloc
,那么调用std::terminate
在这种情况下似乎是合理的。
对我来说,noexcept
是某种形式的文档。这是一个承诺,您(或优化器)可以放心地假设此函数永远不会抛出。如果您正在编写一个不关心内存不足情况的应用程序,那么这仍然是一个有效的假设。
我想最安全的建议是,如果可能引发std::bad_alloc
异常,则永远不要使用noexcept
。另一方面,我想知道使用noexcept
是否有好处,假设您不关心内存不足的情况(即std::terminate
是否可以)。
如果函数可以出于任何原因抛出异常,即使它是std::bad_alloc
,也应该不要将其声明为noexcept
。相对而言,很少有函数真的不能抛出异常,而且它在哪里也很重要。noexcept
函数的主要需求是允许在出现异常时检测可用的错误恢复选项:例如,std::vector<T, A>
在插入对象时可以使用移动构造,假设移动构造不会抛出。如果移动构造可以抛出,则在实现强异常安全操作时,移动对象不能用于恢复异常。因此,如果类型T
的移动构造可能失败,则std::vector<T, A>
的实例化不能移动对象,而是需要复制它们。
特别是,不要使用noexcept
作为虚假文档:如果函数真的可以抛出,那就违反了约定。事实上,在发生这种漏洞的情况下,系统会对某种程度的定义行为做出反应,这并不意味着你应该利用它……虽然简单的程序可能不会恢复,只是在内存耗尽时死亡,但真正的程序可能至少需要存储足够的状态来恢复它们在死亡时留下的混乱,即。,任何函数都不能决定终止程序(当然,除非这是函数的意图)。
我不确定我是否会太担心内存不足的异常。
在一些操作系统(至少是linux)下,当内存耗尽时,默认行为是被操作系统杀死(被oom杀手杀死)。当您向内存写入时(而不是在分配内存时)就会发生这种情况,并且您将没有机会运行任何清理代码。此功能称为内存过度使用
即使你得到了内存耗尽的信息,也很难正确处理这些错误:你需要绝对确保你的异常处理程序不会分配内存。这包括错误处理程序中的所有函数,您还需要确保在此过程中可能触发的任何通用异常处理程序(例如日志记录)都不使用任何内存。你通常希望的最好的办法是在关闭程序之前进行一些简单的清理。
请注意,您也可以使用std::nothrow来检查分配的结果,而不使用异常(也就是说,如果您的操作系统在分配时实际告诉您该信息)。当你做一个你认为可能会失败的大分配时,这样做可能是有意义的。它还有一个很好的特性,即您将获得一个非常容易调试的nullptr,而不是处理(潜在的)未捕获的异常。
- 无法分配函数指针的 2D 数组
- Clang-Format 不能正确分配函数参数
- 我们可以在cpp中为对象分配函数吗
- 分段错误分配函数结果
- 在类内分配函数指针
- 创建一个移动分配函数,不断得到"pointer being freed was not allocated"
- 如何在C++中重新分配函数名称
- 尝试分配函数指针时获取"Void value not ignored as it ought to be"
- 尝试从文件中获取数据时,在C 中的分配函数中没有正确的字符串
- 当使用内存分配函数用作条件语句时会发生什么
- 解除分配函数的 A 函数 B 内的运行时内存
- 是否可以编写一个自定义 STL 分配器,该分配器使用指向用户提供的分配函数的指针
- 当分配函数指针时,这两种符号之间有什么区别
- 新的运算符分配函数顺序连续性和初始值
- 在类中分配函数指针会在C++中给出值类型错误
- 函数包装中的堆栈分配/函数中的alloca
- 创建线程,在循环中为其分配函数并执行
- 我想我可以理解 N5.3.4 中的 §4140/11,但已扩展的分配函数的概念对我来说是不可理解的
- 析构函数和解分配函数的区别是什么?
- 为什么分配函数使用std::nothrow_t作为引用而不是值