Google C ++风格指南的无例外规则; STL?
Google的C ++风格指南说:“我们不使用例外”。 风格不提及STL关于exception的使用。 由于STL分配器可能会失败,他们如何处理容器抛出的exception?
- 如果他们使用STL,调用者如何通知分配失败? 像
push_back()
或mapoperator[]
STL方法不会返回任何状态码。 - 如果他们不使用STL,他们使用什么容器实现?
他们说, 他们不使用例外,不是没有人应该使用它们。 如果你看他们也写的基本原理:
由于Google中大多数现有的C ++代码都不准备处理exception,因此采用生成exception的新代码相对困难。
通常的遗留问题。 🙁
我们根本不处理由容器抛出的exception,至less在应用程序级别的代码中。
自2008年以来,我一直是Googlesearch工程师。我们经常使用STL容器。 我无法亲自回想起一个主要的失败或者是一个曾经追溯到vector :: push_back()或者map :: operator []失败的bug,我们在这里说“哦,我们必须重写这段代码,失败“或”当然,如果只是我们使用了例外,那么可以避免这种情况。“ 一个进程是否耗尽内存? 是的,但是这通常是一个简单的错误(例如,有人为程序增加了一个大的新数据文件,忘记增加RAM分配),或者没有好的方法恢复和继续的灾难性故障。 我们的系统已经自动pipe理和重新启动作业,对于有故障的磁盘,宇宙射线等机器来说是稳健的,这实际上没有什么不同。
所以据我所知,这里没有问题。
我很确定他们的意思是他们不在代码中使用exception。 如果你签出他们的cpplint脚本 ,它会检查以确保你包含STL容器的正确头文件(如向量,列表等)。
我发现Google明确提到STL和exception(重点是我的):
虽然你不应该在自己的代码中使用exception,但是它们被广泛地用在ATL和一些STL中,包括Visual C ++自带的。 使用ATL时,应该定义_ATL_NO_EXCEPTIONS来禁用exception。 您应该调查是否也可以禁用STL中的exception,但是如果不是,则可以在编译器中打开exception。 (注意这只是为了让STL编译, 你还是不要自己编写exception处理代码。 )
我不喜欢这样的决定(幸运的是,我不是为谷歌工作),但他们的行为和意图是非常清楚的。
现代操作系统无法处理分配失败; 作为性能优化,他们通常会过度提交内存。 例如,如果调用malloc()
并要求在Linux上占用大量内存, 即使支持实际所需的内存不在那里 ,它也会成功。 只有当你访问它时,内核实际上会试图分配页面来支持它,在这个时候,告诉你分配失败已经太迟了。
所以:
-
除特殊情况外,不要担心分配失败。 如果机器内存不足,这是一个灾难性的故障,您无法可靠地恢复。
-
尽pipe如此,捕获未处理的exception并logging
e.what()
输出然后重新throw
是个好习惯,因为这可能比回溯更具信息性,而典型的C ++库实现不会自动为您执行。 -
上面的整个巨大的线程关于如何当内存用完时不能依靠崩溃是完全和彻底的垃圾。 C(++)标准可能不能保证它,但在现代系统崩溃是唯一可以依靠,如果你内存不足 。 尤其是,你不能依赖从你的分配器得到一个
NULL
或者任何其他的指示,直到包含一个C ++exception。 -
如果您发现自己在可以访问页面0的embedded式系统上,我强烈build议您通过在该位置映射无法访问的页面来解决这个问题。 不能依靠人类去检查任何地方的
NULL
指针,但是你可以通过映射一个页面来修正这个问题,而不是试图去纠正某个可能错过NULL
每个可能的(过去,现在和将来的)位置。
我将通过说有可能使用某种自定义分配器,或者你在一个不会过度提交的系统(embedded式系统没有交换是一个例子,但不是唯一的例子)例)。 在这种情况下,也许可以在系统上正常处理内存不足的情况。 但总的来说,在21世纪,恐怕你不可能有机会; 首先你会知道你的系统内存不足是当事情开始崩溃的时候。
Stl本身就是直接抛出内存分配失败的情况。 但通常情况下,真实世界的应用程序可能因各种原因失败,内存分配失败只是其中之一。 在32位系统上,内存分配失败不是应该被忽略的事情,因为它可能发生。 所以上面的整个讨论内存分配失败不会发生是没有意义的。 即使这样做,人们将不得不写一个代码使用两步初始化。 C ++exception处理早在64位体系结构之前就已经存在了。 我不确定我应该在多大程度上尊重谷歌所显示的负面专业精神,只回答所提问题。 我记得IBM在1997年左右发表的一些论文,指出IBM的一些人对C ++exception处理的含义了如指掌。 好的专业性不是成功的一个指标。 所以放弃exception处理不仅是一个使用STL的问题。 如果使用C ++的话,这是一个问题。 这意味着放弃
- 构造函数失败
- 能够使用成员对象和基类对象作为任何以下基类/成员类构造函数的参数(没有任何testing)。 难怪人们在C ++exception处理之前使用了两步构造。
- 在允许代码由客户或第三方提供并抛出错误的环境中放弃分层的和丰富的错误消息,在编写调用代码时,调用代码的原作者不可能预见到错误消息,并且可以提供空间在他的返回错误代码范围内。
- 避免了像FlexLm的作者那样向静态内存对象返回指向消息分配失败的指针
- 能够使用内存分配器将地址返回到内存映射的稀疏文件中。 在这种情况下,当访问有问题的内存时会发生分配失败(当然,目前这只能在windows上运行,但是苹果强迫gnu团队在G ++编译器中提供必要的function,还需要Linux g ++开发者提供更多的压力来提供这个function也为他们)(哎呀 – 这甚至适用于STL)
- 能够将这种C风格编码留在我们后面(忽略返回值),并且必须使用debugging器和debugging可执行文件来找出在第三方提供的subprocess和共享库或远程执行的非平凡环境中发生了什么故障
- 能够将丰富的错误信息返回给调用者,而不会将所有内容都倾倒到stderr
在问题中提出的假设下,处理分配失败的唯一可能性是:
- 分配器强制应用程序退出分配失败。 特别是,这需要cusror分配器。
索引超出限制的exception在这种情况下不太有趣,因为应用程序可以确保它们不会使用预检。