是在委托构造函数抛出时自动回收的内存

Is the memory automatically reclaimed when a delegating constructor throws?

本文关键字:内存 构造函数      更新时间:2023-10-16

接下来是:当委托构造函数抛出时,是否调用析构函数?

class X
{
public:
    X()       {};
    X(int)    : X() { throw std::exception(); }
    X(double)       { throw std::exception(); }
    ~X();
};

动态内存呢?通常,构造函数中的异常意味着对象没有完全构造,因此内存是干净的,不会丢失。

但上一个问题中的论点是,在委托完成后,对象被完全构造(或完全初始化)。这对回收内存有何影响?我希望记忆还是干净的!

int main()
{
    new X(5);        // new called 
                     // delete called because delegate completed.
                     // I assume:  
                     //      Memory re-claimed (because constructor did not complete)
                     //      I assume the C++11 standard adjusted to compensate.
                     //      As a constructor did complete.
}

也比较:

int main()
{
    new X(5.0);      // new called 
                     //    Delete **NOT** called
                     // Memory re-claimed (because constructor did not complete)
}

如果内存已清理,则需要从C++03规范更改内存何时清理的定义。行为是如何改变的?

如果new调用的构造函数抛出异常,则new分配的内存将自动解除分配。委派构造函数在这方面没有任何改变。

如果上述对象初始化的任何部分76通过抛出异常而终止,并且可以找到合适的解除分配函数,则调用解除分配函数来释放正在构建对象的存储器

--C++11[expr.new]5.3.4/18

所描述的"对象初始化的任何部分"包括构造函数调用和传递给构造函数的表达式的求值。

此外,这种行为在C++98标准[C++98 5.4.3/17]中有相同的规定。委托构造函数的唯一区别是,你的心理模型以前是否基于正在完全构建的对象。给定的委托构造函数不再等同于何时发生解除分配的实际规范。


在你的第一个例子中:

new X(5);

事件顺序为:

  • 调用了分配函数
  • 调用了X(int)
    • 调用了X()(并成功退出)
    • X(int)引发异常
    • ~X()已调用
  • X(int)通过异常退出
  • 由于对象初始化失败,调用了解除分配函数
  • 异常继续正常传播

对于第二个示例

new X(5.0);
  • 调用了分配函数
  • X(双)调用
  • X(double)失败并出现异常
  • 由于对象初始化失败,调用了解除分配函数
  • 异常继续正常传播

您可以通过替换分配和解除分配函数来观察这种行为:

#include <iostream>
#include <cstdlib>
#include <stdexcept>
#include <new>
    
void *operator new(std::size_t s) {
    if (void *ptr = std::malloc(s)) {
        std::cout << "allocationn";
        return ptr;
    }
    throw std::bad_alloc{};
}
void operator delete(void *ptr) noexcept {
    if (ptr) {
        std::cout << "deallocationn";
        std::free(ptr);
    }
}
struct S {
    S() {};
    S(int) : S{} { throw std::exception(); }
    S(double) { throw std::exception(); }
    ~S() { std::cout << "destructorn"; }
};
int main() {
    std::cout << "test 1n";
    try {
        new S(1);
    } catch(...) {
        std::cout << "exception caughtn";
    }
    std::cout << "test 2n";
    try {
        new S(1.);
    } catch(...) {
        std::cout << "exception caughtn";
    }
}

该程序的正确输出为:

test 1
allocation
destructor
deallocation
exception caught
test 2
allocation
deallocation
exception caught