Google C++风格指南的无例外规则; STL?

And*_*rei 65 c++ stl exception

谷歌的C++风格指南说"我们不使用例外".关于异常的使用,风格没有提到STL.由于STL分配器可能会失败,它们如何处理容器抛出的异常?

  1. 如果他们使用STL,调用者如何获知分配失败?像push_back()map或map operator[]这样的STL方法不会返回任何状态代码.
  2. 如果他们不使用STL,他们使用什么容器实现?

Bo *_*son 53

他们说他们不使用例外,而不是没有人应该使用它们.如果你看一下他们写的基本原理:

由于Google的大多数现有C++代码都没有准备好处理异常,因此采用生成异常的新代码相对比较困难.

通常的遗留问题.:-(

  • 他们的方法也有缺点,比如必须为对象设置所有的Init()函数.怎么忘记一个电话怎么办?你是如何为临时物品做的?不要用那个?您是否为所有类都有一个空的Init()函数,或者只在您发现需要它时添加一个?那你怎么找到你需要添加Init()调用的所有地方?他们说,例外迫使你使用RAII,这应该很难.如果不使用例外,则必须检查所有地方的返回代码或对象有效性.这更难以正确. (12认同)
  • 不只是遗留问题.编写异常安全的代码并不简单,也没有工具(我知道)可以帮助开发人员.因此,即使是新代码也容易出现异常危险. (4认同)
  • 问题是如何*他们*处理该决定对STL容器接口的影响,而不是其他人如何做的;-) (3认同)
  • 问题不在于"他们的决定是否合理,或者是每个人都遵循他们的决定".不,不,不.问题是,是否可以使用stl容器并遵守此规则. (2认同)
  • 让我们公平.在反对例外的6个理由中,只有一个与遗产有关.其他五个"缺点"与遗产没有任何关系. (2认同)

hof*_*anj 43

我们根本处理容器引发的异常,至少在应用程序级代码中是这样.

自2008年以来,我一直是使用C++工作的Google搜索工程师.我们经常使用STL容器.我个人不记得曾经追溯到像vector :: push_back()或map :: operator []失败的单一主要故障或错误,我们说"哦,我们必须重写这段代码,因为分配可能失败"或"dang,如果我们只使用例外,这本来是可以避免的." 进程是否会耗尽内存?是的,但这通常是一个简单的错误(例如,有人向程序添加了一个大的新数据文件,忘了增加RAM分配)或者灾难性的失败,没有好的方法可以恢复和继续.我们的系统已经自动管理和重新启动作业,以便对具有故障磁盘,宇宙射线等的机器保持稳健,这实际上没有什么不同.

据我所知,这里没有问题.

  • @Hinata Hyuga:感谢您的建议,但我同意Loki Astari的意见; 我的回答完全取决于我在谷歌的个人经历,我相信,在回答Stack Overflow问题时,这是一个有效的来源.我已经采取了一些你的语法建议.谢谢,两个! (2认同)
  • @hoffmanj我见过一些使用流的代码,其中底层流进入失败状态,代码无声地失败.每次插入后检查流的状态(ala WinAPI)都是令人讨厌的.当然,样式指南禁止使用流进行非日志记录(另外,还包括stringstream吗?).我想我的问题是,如果你正在与一个STL构造进行交互,除了异常之外没有一个很好的错误条件接口,你会直接禁止它们吗? (2认同)

Mar*_*ser 11

我敢肯定,他们的意思是他们不使用异常代码.如果您检查了他们的cpplint脚本,它会检查以确保您包含STL容器的正确标头(如矢量,列表等).

  • 如"规则的例外"部分所述"在处理不符合此样式指南的代码时,您可能会偏离规则." 当然,STL的设计并未考虑Google惯例. (4认同)
  • Mark,如果你的意思是"他们不会在代码中抛出异常,但是他们允许他们调用的代码抛出异常",这种解释是错误的.点击规则左边的小三角形.从解释来看,很明显他们也不希望从底层代码抛出异常. (3认同)

Yon*_* Wu 6

我发现谷歌明确提到了STL和异常(重点是我的):

虽然您不应该在自己的代码中使用异常,但它们在ATL和一些STL中广泛使用,包括Visual C++附带的STL.使用ATL时,应定义_ATL_NO_EXCEPTIONS以禁用异常.您应该调查是否也可以在STL中禁用异常,但如果没有,则可以在编译器中打开异常.(注意,这只是为了让STL编译.你仍然不应该自己编写异常处理代码.)

我不喜欢这样的决定(幸运的是我不是为谷歌工作),但他们对自己的行为和意图非常清楚.


ala*_*air 6

在现代操作系统上无论如何都无法处理分配失败; 作为性能优化,它们通常会过度提交内存.例如,如果你malloc()在Linux上调用并要求一大块内存,那么即使实际支持它所需的内存不存在,它也会成功.只有当你访问它时,内核实际上才会尝试分配页面来支持它,那时告诉你分配失败已经太晚了.

所以:

  1. 除特殊情况外,不要担心分配失败.如果机器内存不足,那就是灾难性的故障,您无法可靠地恢复.

  2. 尽管如此,捕获未处理的异常并记录e.what()输出然后重新进行是一种很好的做法throw,因为这可能比回溯更具信息性,而典型的C++库实现并不会自动为您执行此操作.

  3. 关于如何在内存不足时不能依赖崩溃的全部大线程是完整的,完全是垃圾.C(++)标准可能无法保证,但在现代系统上,如果内存不足,崩溃是唯一可以依赖的事情.特别是,您不能依赖于NULL从分配器获取或实际上任何其他指示,直到并包含C++异常.

  4. 如果您发现自己位于可访问页面零的嵌入式系统上,我强烈建议您通过在该位置映射无法访问的页面来解决此问题.人类不能依靠其检查NULL到处指针,但你可以通过映射页面修复一次,而不是试图纠正每一个可能的(过去,现在将来)在这有人可能会错过一个位置NULL.

我将通过说你可能正在使用某种自定义分配器,或者你所处的系统没有过度提交(没有交换的嵌入式系统就是一个例子,但不是唯一的例子)来限定上述内容.例).在这种情况下,也许可以在系统上优雅地处理内存不足的情况.但总的来说,在21世纪,我担心你不太可能有机会; 第一个你会知道你的系统内存不足就是事情开始崩溃的时候.


小智 5

Stl本身仅在内存分配失败的情况下直接抛出。但通常现实世界的应用程序可能会因多种原因而失败,内存分配失败只是其中之一。在 32 位系统上,内存分配失败不应该被忽略,因为它可能会发生。因此,上面关于内存分配失败不会发生的整个讨论是毫无意义的。即使假设这一点,人们也必须使用两步初始化来编写代码。C++ 异常处理早于 64 位架构出现很长时间。我不确定我应该在多大程度上尊重谷歌在这里显示的负面专业精神,并且只回答所提出的问题。我记得 1997 年左右 IBM 发表的一篇论文阐述了 IBM 的一些人如何很好地理解和欣赏 C++ 异常处理的含义。好吧,专业精神并不一定是成功的指标。因此,放弃异常处理不仅是使用 STL 的问题。如果像这样使用 C++,那就是一个问题。意味着放弃

  • 构造函数失败
  • 能够使用成员对象和基类对象作为以下任何基/成员类构造函数的参数(无需任何测试)。难怪在 C++ 异常处理出现之前人们就使用了两步构造。
  • 在允许客户或第三方提供代码并抛出错误的环境中放弃分层且丰富的错误消息,而调用代码的原始编写者在编写调用代码时不可能预见到这些错误,并且可以提供空间for 在他的返回错误代码范围内。
  • 避免了诸如返回指向静态内存对象的指针以导致消息分配失败之类的黑客行为,FlexLm 的作者就是这样做的
  • 能够使用内存分配器将地址返回到内存映射稀疏文件中。在这种情况下,当访问有问题的内存时,就会发生分配失败。(好吧,目前这仅适用于 Windows,但苹果强迫 gnu 团队在 G++ 编译器中提供必要的功能。Linux g++ 开发人员将需要提供更多的压力来提供这个功能也适用于他们)(哎呀——这甚至适用于 STL)
  • 能够将这种 C 风格编码抛在脑后(忽略返回值),并且必须使用具有调试可执行文件的调试器来找出具有第三方提供的子进程和共享库或进行远程执行的非平凡环境中出现的问题
  • 能够向调用者返回丰富的错误信息,而无需将所有内容转储到 stderr