【问题标题】:What should be the exception guarantee in a (default) constructor that throws?抛出的(默认)构造函数中的异常保证应该是什么?
【发布时间】:2015-02-28 12:12:05
【问题描述】:

我知道构造函数可以抛出异常,当坏事发生时这是一件好事。但是当构造函数抛出时,并且假设构造函数内部的所有资源都被妥善管理(例如使用RAII),哪一个是确切的异常保证(基本,强)?

详细来说,我正在记录我的代码并编写每个成员函数的异常保证(并尝试编写安全异常代码)。

例如,如果我有这样的课程:

struct A
{
    std::string s;

    A()
    {
        std::vector<int> v(5);
        s = "some text";

        /* do a lot of fascinating things */

        if (error)
            throw 1;
    }
};

当构造函数抛出时,会调用vs 的析构函数,对吧?因此,感谢std::vectorstd::string 的析构函数,A 的构造函数不会泄漏任何资源,然后它至少提供了基本保证。我说的对吗?

我的问题是:我可以说这个构造函数提供了强有力的保证吗? 另外,构造函数的保证是否值得记录?

我的猜测:它确实有很强的保证。由于对象在尝试构造之前不存在,并且如果构造函数失败,则无论如何都不会创建对象,因此操作(构造对象)无效,一切都在构造函数开始之前留下。

如果我猜对了:

  • 当构造函数只提供基本保证而不提供强保证时?

【问题讨论】:

  • “是否值得记录” - 所有函数都应提供异常安全性,因此除非您打算编写糟糕的代码,否则您不需要记录。跨度>
  • @CaptainObvlious 这不是我的意图 ;) 但是知道一个函数的实际保证是非常重要的,因为其他函数可以依赖它来保证自己的保证。我认为记录确切的异常保证并不是一个不好的做法,但我当然对构造函数不太确定。

标签: c++ exception constructor


【解决方案1】:

你是对的。构造函数修改的对象只有sv,它们在构造函数被调用之前不存在,在异常退出后也不存在。因此没有可观察到的副作用,并且构造函数提供了强大的异常保证。

构造函数何时可能只提供基本保证?可能是人为的例子:

class A {
  public:
    A() {
        printf("A is being constructed\n");
        throw std::runtime_error("oh no!");
    }
};

基本保证是很明显的,但不是强保证,因为有副作用。 (产生副作用的另一种方法是修改全局变量。)如果构造函数接受参数,可能会发生更有趣的事情。另一个可能人为的例子:

class B {
  public:
    A(std::vector<int>&& v): v(std::move(v)), a() {}
  private:
    std::vector<int> v;
    A a;
};

这里,A 的构造函数在 B::v 已经初始化之后抛出,因此后者被销毁。调用者仍然有一个有效的向量,但它现在是空的。基本保证仍然满足,因为所有对象都处于有效状态,但不是强保证。

【讨论】:

    【解决方案2】:

    强保证要求如果构造函数抛出,程序的(逻辑)状态不会改变(除非有异常需要处理)。这意味着如果在任何失败情况下没有更改通过引用或指针传递给它的对象、全局变量等(或者在构造函数执行之前已发生的任何更改都回滚),则构造函数满足强异常保证左)。

    您的示例代码中的构造函数不适用于任何此类对象,因此它确实提供了强有力的保证。

    仅提供基本保证的构造函数示例是

    struct foo {
      foo(int &x) : some_resource(10) {
        ++x;
        if(x % 2 == 0) {
          throw "something";
        }
      }
    
      std::vector<int> some_resource;
    };
    

    在这种情况下,基本保证得到了满足——some_resource 在所有情况下都被清除了——但强保证不是因为如果抛出异常,x 仍然会发生变化。

    至于文档,这是一个见仁见智的问题,所以 YMMV。当然,我通常的目标是提供合理可能的最强保证,如果我确定我可以永远保持这个保证,那么我会记录一个函数实现了强大的或无抛出的保证。没有必要记录基本保证,因为所有功能都应该提供它。不提供的功能存在错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-10
      • 1970-01-01
      • 1970-01-01
      • 2010-09-09
      • 2017-11-07
      相关资源
      最近更新 更多