bugprone-multiple-new-in-one-expression¶
在单个表达式中查找多个 new
运算符调用,如果第二个分配失败并抛出异常,则第一个 new
分配的内存可能会泄漏。
C++ 通常不指定运算符操作数或函数参数的精确求值顺序。因此,如果第一个分配成功而第二个分配失败,则在异常处理程序中无法确定哪个分配失败并释放内存。即使顺序已固定,第一个 new
的结果也可能存储在一个在第二个分配失败时无法访问的临时位置。如果使用异常处理来检查分配错误,最好避免任何包含多个 operator new
调用的表达式。
短路运算符 ||
和 &&
以及 ,
运算符适用不同的规则,其中必须完成一边的求值才能开始另一边的求值。列表初始化的表达式(使用 {
和 }
字符进行初始化或构造)按固定顺序求值。类似地,?
运算符的条件将在分支求值之前求值。
如果两个 new
调用出现在运算符的两侧的单个表达式中,或者如果 new
调用出现在函数调用的不同参数中(可以是使用 ()
语法的对象构造),则检查会报告警告。这些 new
调用可以在任何级别嵌套。为了发出任何警告,new
调用应该位于使用 std::bad_alloc
或 std::exception
捕获异常处理的代码块中。在 ||
、&&
、,
、?
(条件和一个分支)运算符中不会发出警告。如果两个内存分配都没有分配给变量或没有直接传递给函数,则不会发出警告。原因是,在这种情况下,内存可能故意没有被释放,或者分配的对象可能是自毁对象。
示例
struct A {
int Var;
};
struct B {
B();
B(A *);
int Var;
};
struct C {
int *X1;
int *X2;
};
void f(A *, B *);
int f1(A *);
int f1(B *);
bool f2(A *);
void foo() {
A *PtrA;
B *PtrB;
try {
// Allocation of 'B'/'A' may fail after memory for 'A'/'B' was allocated.
f(new A, new B); // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; order of these allocations is undefined
// List (aggregate) initialization is used.
C C1{new int, new int}; // no warning
// Allocation of 'B'/'A' may fail after memory for 'A'/'B' was allocated but not yet passed to function 'f1'.
int X = f1(new A) + f1(new B); // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; order of these allocations is undefined
// Allocation of 'B' may fail after memory for 'A' was allocated.
// From C++17 on memory for 'B' is allocated first but still may leak if allocation of 'A' fails.
PtrB = new B(new A); // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception
// 'new A' and 'new B' may be performed in any order.
// 'new B'/'new A' may fail after memory for 'A'/'B' was allocated but not assigned to 'PtrA'/'PtrB'.
(PtrA = new A)->Var = (PtrB = new B)->Var; // warning: memory allocation may leak if an other allocation is sequenced after it and throws an exception; order of these allocations is undefined
// Evaluation of 'f2(new A)' must be finished before 'f1(new B)' starts.
// If 'new B' fails the allocated memory for 'A' is supposedly handled correctly because function 'f2' could take the ownership.
bool Z = f2(new A) || f1(new B); // no warning
X = (f2(new A) ? f1(new A) : f1(new B)); // no warning
// No warning if the result of both allocations is not passed to a function
// or stored in a variable.
(new A)->Var = (new B)->Var; // no warning
// No warning if at least one non-throwing allocation is used.
f(new(std::nothrow) A, new B); // no warning
} catch(std::bad_alloc) {
}
// No warning if the allocation is outside a try block (or no catch handler exists for std::bad_alloc).
// (The fact if exceptions can escape from 'foo' is not taken into account.)
f(new A, new B); // no warning
}