抛出新表达式的参数子表达式时释放内存

Jan*_*dec 9 c++ memory-leaks exception-handling visual-c++ language-lawyer

动态分析在我们的代码库中发现了奇怪的内存泄漏.有问题的代码如下:

Something *p = new Something(getArgument());
Run Code Online (Sandbox Code Playgroud)

功能getArgument()有时会抛出的地方.当它抛出时,新分配的对象被泄露.这是由Visual Studio 2015(MSC++ 19.0)编译的.

现在,当我检查规范(C++ 14最终草案)时,§5.3.4/ 8奇怪地说:

新表达 可以通过调用一个获得所述对象存储分配功能(3.7.4.1).如果 new-expression通过抛出异常终止,它可以通过调用释放函数释放存储(3.7.4.2).如果分配的类型是非数组类型,则分配函数的名称为operator new,并且释放函数的名称为operator delete.如果分配的类型是数组类型,则分配函数的名称为operator new[],并且释放函数的名称为operator delete[].

使用'may'(我上面突出显示)这意味着编译器可以自由地不这样做.

这是这样的:

  1. 在规范中的其他地方按要求声明,使其成为Visual C++编译器中的错误(可能仅在某些条件下发生;不检查它是多么通用),
  2. 规范中的错误,或
  3. 出于某种原因写这种方式?

注意:代码在表达式完成时会正确删除对象.这没有错误.问题严重在于new-expression抛出时会发生什么.

Whi*_*TiM 4

从最新的草案来看,相关引用位于:

\n\n
\n

expr.new/8新表达式 可以通过调用分配函数 ([basic.stc.dynamic.allocation]) 来获取对象的存储空间。如果 new 表达式因抛出异常而终止,则它可以通过调用释放函数来释放存储空间。....

\n
\n\n

“可以”的使用优先于后续部分:

\n\n
\n

expr.new/21如果上述对象初始化的任何部分因抛出异常而终止,并且可以找到合适的释放函数,则调用释放函数来释放该对象所在的内存。正在构造,之后异常继续在 new 表达式的上下文中传播。如果找不到明确\n 匹配的释放函数,则传播异常\n 不会导致对象的内存被释放。[\xe2\x80\x89注意:当被调用的分配函数不分配内存时,这是适当的;否则,很可能会导致内存泄漏。\xe2\x80\x94\xe2\x80\x89尾注\n \xe2\x80\x89]

\n
\n\n
\n\n

但是 C++14 及更早版本中新表达式的不确定排序给您带来了困扰;其中说:

\n\n
\n

$5.3.4/18 分配函数的调用相对于 new-initializer 中表达式的计算是不确定地排序的。\n 已分配对象的初始化在 new-expression 的值计算之前排序。如果分配函数返回空指针或使用异常退出,则未指定是否计算 new 初始化程序中的表达式。

\n
\n\n

摘自 C++14 草案

\n\n

根据本文的通过。现在我们在 C++17 中定义了一个序列:

\n\n
\n

expr.new/19 \n 分配函数的调用在 new-initializer 中的表达式求值之前排序。分配的对象的初始化在 new 表达式的值计算之前进行排序。

\n
\n\n
\n\n
\n