从构造函数中的catch块调用析构函数安全吗?

时间:2019-02-18 23:20:18

标签: c++ constructor try-catch object-lifetime explicit-destructor-call

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

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

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

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

2 个答案:

答案 0 :(得分:4)

尽管析构函数看起来像普通方法,而显式销毁语法看起来像是该方法的调用,但实际上并不仅仅是调用该方法。除其他特定于实现的内容外,它还调用基类和数据成员的析构函数。从构造函数中抛出异常还会导致所有这些析构函数被调用。因此,~Object()后跟throw将对其进行两次调用,可能会造成灾难性的后果。

就像有人在注释中建议的那样,只需将清除代码移至普通方法即可。

函数调用语法(用于构造临时对象)以及new / deleteoperator new / operator delete也存在类似的句法问题。即使看起来像他们一样,它们中没有一个会以相同的名称调用这些函数。

答案 1 :(得分:0)

首先,invoking a member function here is fine

  

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

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

但是随后有this具体涉及析构函数:

  

一旦为一个对象调用了析构函数,该对象就不再存在;如果为生存期已结束的对象([basic.life])调用析构函数,则该行为未定义。 [示例:如果显式调用了自动对象的析构函数,并且随后以通常会调用对象的隐式破坏的方式保留该块,则该行为是不确定的。 -示例]

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

在这一点上,我实际上已经放弃了标准语言,并且我想知道这是否有些不足。我的观点是,这本身可能就足以避免这种意图良好的模式,只需将您的清除工作放到一个不错的私有成员函数中,即可在您的catch块和析构函数之间共享。