当对象构造函数抛出一个新表达式时,为什么不调用deallocation函数?

时间:2017-09-01 08:02:25

标签: c++ new-operator dynamic-memory-allocation delete-operator

如果我将operator delete定义为如下,并且如果对象构造函数抛出新表达式,我希望看到调用定义运算符的结果delete:

#include <new>
#include <cstdlib>
#include <iostream>

void*
operator new(std::size_t s){
  std::cout << "alloc " << std::endl;
return std::malloc(s);
}

void
operator delete(void* p) noexcept {
  std::cout << "dealloc " << std::endl;
  std::free(p);
}
void
operator delete(void* p,std::size_t) noexcept{
    std::free(p);
    std::cout << "dealloc s" << std::endl;
}

struct A{
  A(int i){
     if(i>0)
       throw 10;
  }
};

int main(int argc// will equal 10
        ,char* arg[])
{
  for(int i=0;i<argc;++i)
     auto p=new A{argc};
  return 0;
}

这个程序只输出alloc,为什么不调用operator delete?在标准[expr.new]中指定:

  

如果上述对象初始化的任何部分通过抛出异常和合适的终止而终止   可以找到deallocation函数,调用deallocation函数来释放对象所在的内存   正在构建,之后异常继续在new-expression的上下文中传播。

3 个答案:

答案 0 :(得分:2)

正如其他人已经指出的那样,这是因为你没有抓住异常。正如标准所说:

  

C ++11§15.3/ 9
  “如果找不到匹配的处理程序,则调用函数std::terminate();在对std::terminate()的调用是否为实现定义之前,堆栈是否已展开。“

虽然我认为这与你的情况下的堆栈没有特别的关系,但同样的原则也会存在。因此,如果清理了任何内存,实际上取决于实现。正如我们在这里看到的那样,通常情况并非如此,因为无论如何操作系统都会清理内存。

答案 1 :(得分:1)

如果您修复代码以抛出异常,它会按预期工作:

int main(int argc,char* arg[])
{
    try {
        new A(2);
    }
    catch (...)
    {}
}

Demo

答案 2 :(得分:1)

如果在构造期间使用new,构造函数会抛出异常,即C ++运行时库:

    如果找不到合适的catch处理程序,则
  1. 调用std::terminate()。是否调用delete是实现定义的。
    1. 在将“异常”发送到合适的catch处理程序之前为你调用delete,虽然没有调用析构函数 - 即~A(),如果有的话,将不会被调用当i大于1时。