我知道构造函数可以抛出异常,当发生错误时,这可能是件好事。但是当构造函数抛出时,并假设构造函数中的所有资源都得到了适当的管理(例如使用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;
}
};
当构造函数抛出时,v
和s
的析构函数被调用,对吧?所以,由于std::vector
和std::string
的析构函数,A
的构造函数不泄漏任何资源,然后它至少提供了基本保证。我是对的吗?
我的问题是:我可以说这个构造函数提供了强有力的保证吗? 另外,是否值得记录构造函数的保证?
我的猜测:它确实有很强的保证。由于在尝试构造对象之前该对象不存在,并且如果构造函数失败,则无论如何都不会创建对象,那么操作(构造对象)没有任何效果,并且在构造函数启动之前就保留了所有内容。
如果我猜对了:
答案 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)
你是对的。构造函数修改的唯一对象是s
和v
,它们在构造函数被调用之前不存在,并且在异常退出后它们不存在。因此没有可观察到的副作用,构造函数提供强大的异常保证。
构造函数何时才能提供基本保证?可能是做作的例子:
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
已经初始化之后抛出,因此后者被销毁。调用者仍然有一个有效的向量,但它现在是空的。由于所有对象都处于有效状态,但基本保证仍然满足,但不是强有力的保证。