4

在我的构造函数中,如果其中的任何代码抛出,我必须销毁任何剩余的资源。我想避免编写重复的代码,所以我只在 catch 块中调用析构函数,而不是释放任何已创建的资源。这安全吗?

我知道如果构造函数抛出,则不会调用析构函数,因此我尝试在 msvc 中编译一些代码,但似乎没有任何问题,但我不确定这是否只是运气。

Object::Object(){
    try{
        // Initialize multiple resources here.
    }catch(...){
        this->~Object(); // Is this safe?
        throw;
    }
}

Object::~Object(){
    // release multiple resources, if initialized.
}
4

2 回答 2

4

尽管析构函数看起来像普通方法,并且显式的析构语法看起来像对该方法的调用,但它实际上并不只是调用该方法。在其他特定于实现的事情中,它还调用基类和数据成员的析构函数。从构造函数中抛出异常也会导致调用所有这些析构函数。所以,~Object()后面throw会打电话给他们两次,可能会带来灾难性的后果。

正如有人在评论中建议的那样,只需将清理代码移至普通方法即可。

new用于构造临时的函数调用语法和/deleteoperator new/也存在类似的语法问题operator delete。它们中没有一个只是调用具有相同名称的函数,即使看起来它们应该这样做。

于 2019-02-19T00:23:10.787 回答
1

首先,在这里调用成员函数很好

成员函数,包括虚函数 ([class.virtual]),可以在构造或销毁 ([class.base.init]) 期间调用。

(您的构造函数已开始执行。)

但是还有关于析构函数的具体内容:

一旦为对象调用析构函数,该对象就不再存在;如果为生命周期已结束的对象([basic.life])调用析构函数,则行为未定义。[ 示例:如果自动对象的析构函数被显式调用,并且块随后以通常会调用对象的隐式销毁的方式离开,则行为未定义。—结束示例]

因此,尽管我们确实知道您的析构函数不会被“再次”隐式调用,但问题是随后的重新抛出是否会导致对象“再次”在本段描述的意义上被破坏。

在这一点上,我实际上放弃了标准语,我想知道这是否有点低估了。我的观点是,这本身可能足以避免这种善意的模式,只需将您的清理放在一个不错的私有成员函数中,以便在您的catch块和析构函数之间共享。

于 2019-02-18T23:34:48.593 回答