构造函数会影响封闭对象的其他字段,还是静态分析误报?
Can a constructor affect other fields of an enclosing object, or is this a static analysis false positive?
考虑这个 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
时,我得到 this:
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
s b
编辑它的代码也是如此:
struct foo{
const bool b=true;
foo(){ const_cast<bool&>(b)=false; }
};
(第二个版本编译,但生成 UB)。
遗憾的是,这样的 UB 并不罕见。例如,我可以在 this
指针地址之前用内存实现 SomeStruct
到 fiddle 的构造函数。这将是双重非法的(在构造后修改 const
值,并违反可达性规则),但取决于优化设置它可以工作。
另一方面,编译器可以自由地注意到唯一的构造函数将 true
分配给 b
,然后将 operator bool
转换为 return true
.
但是,一旦调用可见源代码之外的函数体,静态代码分析器就会放弃证明 b
的状态。放弃这是一件非常合理的事情。在这里,该函数甚至获得了指向同一个临时对象的指针;做一个完整的证明,无论代码是什么,指针都不能改变某些状态 运行 是可能的,但不这样做似乎也是合理的。
风格不错,代码也有点乱。一个可证明的真实分支要么不应该存在,要么失败分支在语义上应该是有意义的。两者都不会发生在这里;任何阅读此代码的人都无法从代码结构中确定正确性;代码结构看起来具有误导性。
考虑这个 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
时,我得到 this:
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
s b
编辑它的代码也是如此:
struct foo{
const bool b=true;
foo(){ const_cast<bool&>(b)=false; }
};
(第二个版本编译,但生成 UB)。
遗憾的是,这样的 UB 并不罕见。例如,我可以在 this
指针地址之前用内存实现 SomeStruct
到 fiddle 的构造函数。这将是双重非法的(在构造后修改 const
值,并违反可达性规则),但取决于优化设置它可以工作。
另一方面,编译器可以自由地注意到唯一的构造函数将 true
分配给 b
,然后将 operator bool
转换为 return true
.
但是,一旦调用可见源代码之外的函数体,静态代码分析器就会放弃证明 b
的状态。放弃这是一件非常合理的事情。在这里,该函数甚至获得了指向同一个临时对象的指针;做一个完整的证明,无论代码是什么,指针都不能改变某些状态 运行 是可能的,但不这样做似乎也是合理的。
风格不错,代码也有点乱。一个可证明的真实分支要么不应该存在,要么失败分支在语义上应该是有意义的。两者都不会发生在这里;任何阅读此代码的人都无法从代码结构中确定正确性;代码结构看起来具有误导性。