10

我是一名学生,我对 C++ 知之甚少,我试图扩展这些知识。这更像是一个哲学问题..我不是要实施什么。

自从

#include <new> 
//...
T * t = new (std::nothrow) T();
if(t)
{
    //...
}
//...

将隐藏异常,并且由于处理异常比简单的更重if(t)new T()考虑到我们将不得不使用try-catch()检查简单分配是否成功(如果我们不成功,只是看着程序死)??

new与使用 a 相比,正常分配有什么好处(如果有的话)nothrow new?在这种情况下异常的开销是微不足道的吗?

此外,假设分配失败(例如,系统中不存在内存)。在那种情况下程序有什么可以做的,或者只是优雅地失败。没有办法在堆上找到空闲内存,当所有的都被保留时,有吗?

万一分配失败,并且 anstd::bad_allocthrown,我们如何假设由于没有足够的内存来分配对象(例如 a new int),所以会有足够的内存来存储异常?

谢谢你的时间。我希望这个问题符合规则。

4

6 回答 6

10

由于与简单的 if(t) 相比,处理异常更重,为什么不认为普通的 new T() 不是不太好的做法,考虑到我们将不得不使用 try-catch() 来检查简单的分配是否成功(如果我们不这样做,就看着程序死掉)??与使用 nothrow new 相比,正常的新分配有什么好处(如果有的话)?在这种情况下异常的开销是微不足道的吗?

使用异常的惩罚确实很重,但是(在经过适当调整的实现中)只有在抛出异常时才会支付惩罚- 所以主线案例保持非常快,并且两者之间不太可能有任何可衡量的性能你的例子。

异常的优点是您的代码更简单:如果分配多个对象,您不必执行“分配 A; if (A) { allocate B; if (B) etc...”。清理和终止 - 在异常和主线情况下 - 最好由 RAII 自动处理(而如果您手动检查,您还必须手动释放,这很容易泄漏内存)。

此外,假设分配失败(例如,系统中不存在内存)。在那种情况下程序有什么可以做的,或者只是优雅地失败。没有办法在堆上找到空闲内存,当所有的都被保留时,有吗?

它可以做很多事情,而最好的事情将取决于正在编写的程序。失败和退出(优雅地或以其他方式)当然是一种选择。另一种是提前预留足够的内存,以便程序可以继续执行其功能(可能会降低功能或性能)。它可能能够释放一些自己的内存(例如,如果它维护可以在需要时重建的缓存)。或者(在服务器进程的情况下),服务器可能拒绝处理当前请求(或拒绝接受新连接),但保持运行,以便客户端不会断开连接,一旦内存,事情就可以重新开始工作返回。或者在交互式/GUI 应用程序的情况下,

万一分配失败,并且抛出 std::bad_alloc,我们怎么能假设因为没有足够的内存来分配一个对象(例如一个新的 int),所以会有足够的内存来存储一个异常?

不,通常标准库通常会通过提前分配少量内存来确保在内存耗尽时有足够的内存来引发异常。

于 2010-12-31T18:47:53.903 回答
6

Nothrow 被添加到 C++ 中主要是为了支持想要编写无异常代码的嵌入式系统开发人员。如果您确实想在本地处理内存错误作为比 malloc() 后跟放置 new 更好的解决方案,它也很有用。最后,对于那些希望继续使用(当时是当前的)基于检查 NULL 的 C++ 编程风格的人来说,这是必不可少的。[我自己提出了这个解决方案,这是我提出的少数几个没有被否决的事情之一:]

仅供参考:在内存不足时抛出异常是非常设计敏感且难以实现的,因为例如,如果你要抛出一个字符串,你可能会因为字符串进行堆分配而出现双重错误。事实上,如果你的堆崩溃到堆栈中而导致内存不足,你甚至可能无法创建一个临时的!这个特殊情况解释了为什么标准例外受到相当的限制。此外,如果您在本地捕获这样的异常,为什么您应该通过引用而不是通过值来捕获(以避免可能的副本导致双重错误)。

正因为如此,nothrow 为关键应用提供了更安全的解决方案。

于 2011-01-01T02:18:46.427 回答
4

我认为为什么使用常规new而不是nothrow新的原因与为什么通常首选异常而不是显式检查每个函数的返回值的原因有关。如果找不到内存,并不是每个需要分配内存的函数都一定知道该怎么做。例如,一个将内存作为子程序分配给某个算法的深度嵌套函数可能不知道如果找不到内存,应该采取什么正确的行动。使用一个版本new引发异常的代码允许调用子例程的代码(而不是子例程本身)采取更适当的行动。这可能很简单,就像什么都不做,看着程序死掉(如果你正在编写一个小玩具程序,这很好),或者发出一些更高级别的程序构造开始丢弃内存的信号。

关于你问题的后半部分,如果你的程序内存不足,你实际上可以做一些事情,这会使内存更可用。例如,您的程序可能有一部分缓存旧数据,并且可以告诉缓存在资源紧张时立即驱逐所有内容。您可能会将一些不太重要的数据分页到磁盘,这可能比您的内存有更多的空间。有很多这样的技巧,通过使用异常,可以将所有紧急逻辑放在程序的顶部,然后让程序的每个执行分配的部分都不会捕获 bad_alloc 而是让它传播到顶部。

最后,即使内存不足,通常也可以抛出异常。许多 C++ 实现在堆栈(或其他一些非堆内存段)中为异常保留一些空间,因此即使堆空间不足,也可以为异常找到内存。

希望这可以帮助!

于 2010-12-31T18:30:34.877 回答
3

因为它们“太昂贵”而绕过异常是过早的优化。如果没有抛出异常,则几乎没有 try/catch 的开销。

在那种情况下程序有什么可以做的吗

通常不会。如果系统中没有内存,您甚至可能无法将任何内容写入日志,或打印到标准输出或任何内容。如果你的记忆力不足,那你就完蛋了。

于 2010-12-31T18:26:49.613 回答
2

内存不足预计是一种罕见的事件,因此在发生异常时引发异常的开销不是问题。实现可以“预分配”抛出 a 所需的任何内存std::bad_alloc,以确保即使在程序内存不足的情况下它也可用。

默认情况下抛出异常而不是返回 null 的原因是它避免了每次分配后都需要进行 null 检查。许多程序员不会这样做,如果程序在分配失败后继续使用空指针,它可能会在稍后崩溃并出现类似分段错误的情况,这并不能说明问题的真正原因。使用异常意味着如果 OOM 条件没有得到处理,程序将立即终止并返回一个错误,该错误实际上表明出了什么问题,这使得调试更加容易。

如果它们抛出异常,编写内存不足情况的处理代码也更容易:不必单独检查每个分配的结果,您可以catch在调用堆栈中的某个较高位置放置一个块,以从许多地方捕获 OOM 条件。该程序。

于 2010-12-31T18:36:59.730 回答
-1

在 Symbian C++ 中,它以相反的方式工作。如果你想在 OOM 时抛出异常,你必须这样做

T* t = new(ELeave) T();

当 OOM 很奇怪时,您对抛出新异常的逻辑是正确的。一个可以管理的场景突然变成了程序终止。

于 2010-12-31T18:31:59.093 回答