抛出新表达式的参数子表达式时释放内存
Deallocation of memory when argument subexpression of new expression throws
动态分析在我们的代码库中发现了奇怪的内存泄漏。有问题的代码如下所示:
Something *p = new Something(getArgument());
函数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[]
。
在这里使用"可能"(我在上面突出显示)意味着编译器可以自由地不这样做。
这也是这样的:
- 在规范中的其他地方根据需要声明,使其成为Visual C++编译器中的错误(可能仅在某些条件下发生;没有检查它的通用性),
- 规范中的错误,或
- 出于某种原因这样写?
注意:当表达式完成时,代码会正确删除对象。这其中没有错误。问题严格在于新表达式抛出时会发生什么。
从最新草案来看,相关报价如下:
expr.new/8:新表达式可以通过调用分配函数来获取对象的存储 ([basic.stc.dynamic.allocation])。如果新表达式的终止时间为 引发异常,它可能会通过调用 释放功能。....
"可以"的使用优先于程序部分:
expr.new/21 如果 上述对象初始化通过抛出 异常和合适的释放函数可以找到, 调用释放函数以释放对象所在的内存 正在构建中,之后异常继续 在新表达式的上下文中传播。如果没有明确的 可以找到匹配的释放函数,传播异常 不会导致释放对象的内存。[ 注意:这是 当调用的分配函数未分配时适用 记忆;否则,可能会导致内存泄漏。— 尾注 ]
但是你被 C++14 及更早的新表达的不确定序列击中了;其中说:
$5.3.4/18 分配函数的调用是不确定排序的 关于新初始值设定项中表达式的计算。 分配对象的初始化在值之前排序 新表达式的计算。目前尚不清楚是否 如果分配,则计算新初始值设定项中的表达式 函数返回空指针或使用异常退出。
摘自 C++14 草案
根据本文的通过。 我们现在在 C++17 中有一个定义的序列:
expr.new/19 分配函数的调用在 新初始值设定项中表达式的计算。初始化 分配的对象在计算 新表达。
进一步检查 - 特别是第 20 段:
如果上述对象初始化的任何部分通过抛出异常而终止,并且可以找到合适的释放函数,则调用释放函数以释放构造对象的内存,之后异常继续在新表达式的上下文中传播。如果找不到明确的匹配释放函数,则传播异常不会导致释放对象的内存。[ 注意:当调用的分配函数不分配内存时,这是合适的;否则,可能会导致内存泄漏。— 尾注 ]
- 释放错误后堆使用
- (C++)分析树以计算返回错误值的简单算术表达式
- 在VS2010-VS2015下编译时,如何使用decltype作为较大类型表达式的LHS
- 提升精神:解析布尔表达式并简化为规范范式
- 不能在初始值设定项列表中将非常量表达式从类型 'int' 缩小到'unsigned long long'
- G锁定铸造到基础上会释放模拟行为
- 在将变量声明为引用时,堆在释放后使用
- 使用正则表达式regex_search在字符串中查找字符串
- 在调用FreeLibrary后,释放动态链接到具有相同版本的CRT堆的DLL的内存
- 如何确认我的constexpr表达式实际上已经在编译时执行
- 概念中的cv限定符需要表达式参数列表
- 为什么constexpr的性能比正常表达式差
- 对于结构,表达式必须是可修改的ivalue
- 当一个值是非常量但用常量表达式初始化时使用constexpr
- 将fold表达式与std::一起用于两个元组
- 断言中的Fold表达式在某些计算机上编译,但在其他计算机上不编译
- 标记 '","' 之前的预期主表达式
- 抛出新表达式的参数子表达式时释放内存
- 有什么方法可以通过删除表达式安全地处理两次释放内存?
- 释放用户定义表达式析构函数中的动态内存