考虑以下课程:
struct A
{
A(){ std::cout << "A()" << std::endl; throw std::exception(); }
~A(){ std::cout << "~A()" << std::endl; }
};
A a;
int main(){ }
标准承诺将调用适当的释放函数N4296::5.3.4/20 [expr.new]
:
如果上述对象初始化的任何部分78终止 通过抛出异常,已经获得了对象的存储, 并且可以找到合适的解除分配函数,即释放 调用函数来释放对象所在的内存 构造
但是析构函数呢?在那个例子中没有被召唤。那么,我们得到了UB吗?
答案 0 :(得分:2)
为所有成功初始化的对象调用析构函数。
否则必须进行默认的零初始化(一些开销),以便能够在析构函数中假设任何内容。
构造函数抛出的A对象未成功初始化。所以它的析构函数没有被执行。但是,如果它具有在异常之前已成功初始化的任何类类型子对象(基类子对象,数据成员),则会为这些子对象调用析构函数。
不,从构造函数中抛出的不是UB。
相反,它是信令构造失败的常见方式,它确保调用者要么具有成功初始化且可能是可用的对象,要么(只是)异常。
历史。最初C ++没有例外。然后通过将0分配给this
来表示施工失败。我不记得它是如何与分配相互作用的,但可能与异常现在的方式相同,即保证解除分配。但是使用这种方案,你只能为动态分配的对象构建失败......
答案 1 :(得分:0)
析构函数与解除分配器不同。每当变量超出范围时,将删除引用该变量的对象。在你给出的问题中,对象是一个全局变量,所以当程序终止时它将被删除。