14

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

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 规范中改变。行为如何改变?

4

1 回答 1

21

如果被调用的构造函数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(double) 调用
  • 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 << "allocation\n";
        return ptr;
    }
    throw std::bad_alloc{};
}

void operator delete(void *ptr) noexcept {
    if (ptr) {
        std::cout << "deallocation\n";
        std::free(ptr);
    }
}

struct S {
    S() {};
    S(int) : S{} { throw std::exception(); }
    S(double) { throw std::exception(); }
    ~S() { std::cout << "destructor\n"; }
};

int main() {
    std::cout << "test 1\n";
    try {
        new S(1);
    } catch(...) {
        std::cout << "exception caught\n";
    }

    std::cout << "test 2\n";
    try {
        new S(1.);
    } catch(...) {
        std::cout << "exception caught\n";
    }
}

这个程序的正确输出是:

test 1
allocation
destructor
deallocation
exception caught
test 2
allocation
deallocation
exception caught
于 2013-07-15T16:29:32.727 回答