抛出的(默认)构造函数中的异常保证应该是什么?

时间:2015-01-02 04:46:20

标签: c++ exception constructor

我知道构造函数可以抛出异常,当发生错误时,这可能是件好事。但是当构造函数抛出时,并假设构造函数中的所有资源都得到了适当的管理(例如使用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的构造函数不泄漏任何资源,然后它至少提供了基本保证。我是对的吗?

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

我的猜测:它确实有很强的保证。由于在尝试构造对象之前该对象不存在,并且如果构造函数失败,则无论如何都不会创建对象,那么操作(构造对象)没有任何效果,并且在构造函数启动之前就保留了所有内容。

如果我猜对了:

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

2 个答案:

答案 0 :(得分: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。当然,我通常会提供最合理可能的最强保证,并且如果我确信我可以永远保持这种保证,那么证明某项功能可以实现强有力或无投保。记录基本保证不是必要的,因为所有功能都应提供它。没有提供它的函数有一个错误。

答案 1 :(得分:2)

你是对的。构造函数修改的唯一对象是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已经初始化之后抛出,因此后者被销毁。调用者仍然有一个有效的向量,但它现在是空的。由于所有对象都处于有效状态,但基本保证仍然满足,但不是强有力的保证。