考虑下面的c++代码:
struct SomeStruct {
SomeStruct() noexcept;
};
//SomeStruct::SomeStruct() noexcept {}
class SomeClass {
const bool b;
const SomeStruct s;
public:
SomeClass() : b(true) {}
operator bool() const { return b; }
};
void f() {
int *p = new int;
if (SomeClass())
delete p;
}
当我在上面运行clang --analyze -Xanalyzer -analyzer-output=text
时,我得到这个:
q72007867.cpp:20:1: warning: Potential leak of memory pointed to by 'p' [cplusplus.NewDeleteLeaks]
}
^
q72007867.cpp:17:12: note: Memory is allocated
int *p = new int;
^~~~~~~
q72007867.cpp:18:7: note: Assuming the condition is false
if (SomeClass())
^~~~~~~~~~~
q72007867.cpp:18:3: note: Taking false branch
if (SomeClass())
^
q72007867.cpp:20:1: note: Potential leak of memory pointed to by 'p'
}
^
1 warning generated.
取消注释SomeStruct
的构造函数的定义可以使警告消失。交换const bool b;
和const SomeStruct s;
的顺序也会使其消失。在原始程序中,是否存在SomeStruct
构造函数的其他定义,这将导致在那里采取假分支,或者这是Clang的静态分析器中的假阳性?
const
成员初始化后修改没有标准的兼容方式;任何机制都是UB
struct foo{
const bool b=true;
foo(){ b=false; }
};
是非法的,就像const_cast
和b
编辑的代码一样:
struct foo{
const bool b=true;
foo(){ const_cast<bool&>(b)=false; }
};
(第二个版本编译,但生成UB)。
遗憾的是,这样的UB并不罕见。例如,我可以实现SomeStruct
的构造函数,在this
指针地址之前篡改内存。这将是双重非法的(在构造之后修改const
值,并且违反可达性规则),但根据优化设置,它可以工作。
另一方面,编译器可以自由地注意到唯一的构造函数将true
赋值给b
,然后将operator bool
转换为只返回true
。
但是,一旦在可见源代码之外发生对函数体的调用,静态代码分析器就会放弃检测b
的状态。这是一个很合理的放弃。这里,函数甚至获得指向同一个临时对象的指针;做一个完整的证明,无论运行什么代码,指针都不能改变某些状态是可能的,但不这样做似乎也是合理的。
样式方面,代码也有点乱。一个可证明为真的分支不应该存在,或者失败分支应该在语义上有意义。这两种情况在这里都没有发生;任何阅读这段代码的人都不能从代码结构中判断正确性;代码结构看起来很容易误导。