Effective c++ 条款29:为“异常安全”而努力是值得的

当异常被抛出时,带有异常安全性的函数会:

  • 不泄露任何资源
  • 不允许数据败坏

异常安全函数提供以下三个保证之一:

  • 基本承诺:如果异常被抛出,程序内的任何事物仍然保持在有效状态下。没有任何对象或数据结构会因此而败坏(但不一定与原来的一样),所有对象都除于一种内部前后一致的状态。然而程序的现实状态恐怕不可预料。
  • 强烈保证:如果异常被抛出,程序状态不改变。调用这样的函数需有这样的认知:如果函数成功,就是完全成功;如果函数失败,程序会回复到“调用函数之前”的状态。跟基本承诺形成对比的是,如果调用只提供基本承诺的函数,而真的出现异常,程序有可能处于任何状态–只要那是个合法状态。
  • 不抛掷保证:承诺决不抛出异常,因为它们总是能够完成它们圆形承诺的功能。作用于内置类型身上的所有操作都提供nothrow保证。这是异常安全码中必不可少的关键基础材料。

异常安全码必须提供上述三种保证之一。如果它不这样做,他就不具备异常安全性。因此,我们的抉择是,该为我们所写的每一个函数提供哪一种保证?
一般而言你应该会想提供可实施的最强烈保证。从异常安全性的观点视之,nothrow函数很棒,但我们很难在c part of c++领域中完全没有调用任何一个可能抛出异常的函数。任何使用动态内存的东西如果无法找到足够内存以满足需求,通常便会抛出y一个bad_alloc异常。因此,对大多数函数而言,抉择往往落在基本保证和强烈保证之间。

有一个一般化的设计策略很典型地会导致强烈保证,很值得熟悉它。这个策略称为copy and swap。原则很简单,为你打算修改的对象作出一份副本,然后在那副本身上做一切必要修改。若有任何修改动作抛出异常,原对象仍然保持未改变状态。待所有改变都成功后,再将修改后的那个副本和原对象在一个不抛出异常的操作中置换。

copy and swap策略是对对象状态做出“全有或全无”改变的一个很好办法,但一般而言它并不保证整个函数有强烈的异常安全性。例如,当某个函数内还包括对另外两个函数f1()。f2()的调用,如果f1或f2的异常安全性比“强烈保证”低,就很难让该函数成为“强烈异常安全”。问题出在“连带影响”。当函数对“非局部性数据”有连带影响时,提供强烈保证就困难得多。

另一个问题是效率。copy and swap的关键在于修改对象数据的副本,然后在一个不抛异常的函数中将修改后的数据和原件置换,因此不必须为每一个即将被改动的对象作出一个副本,那得耗用你可能无法供应的时间和空间。

当“强烈保证”不切实际时,你就必须提供“基本保证”。然而如果你写的函数完全不提供异常安全保证,情况又有点不同,假设f2完全没有提供异常安全保证,连基本保证都没有,那就意味着一旦f2抛出异常,程序可能在f2内泄露资源,f2可能败坏数据结构。如果函数调用的函数没有提供任何异常安全保证,该函数自身也不可能提供任何保证。

没有理由让这种情况永垂不朽。当你撰写新码或修改旧码时,请仔细想想如何让它具备异常安全性。首先是以对象管理资源,那可组织资源泄露。然后是挑选三个“异常安全保证”中的某一个实施于你所写的每一个函数上。你应该挑选“现实可施行”条件下最强烈登记,只有当你的函数调用了传统代码,才别无选择地将它设为“无任何保证”。将你的决定写成文档,这一来是为你的函数用户着想,二来是为将来的维护者着想。

猜你喜欢

转载自blog.csdn.net/unirrrrr/article/details/81330028