V595诊断逻辑很简单。如果在开始时取消引用指针,则会发出警告,然后验证是否与 nullptr 相等。
当然,在许多情况下,分析仪会在遇到这种模式时保持安静。包括指针不等于 nullptr 的情况,因此分析器将保持安静。
但是,Q_ASSERT(_parent)
不保证指针_parent
非零。如果_parent
为零,Q_ASSERT语句将使用qFatal函数输出以下消息。如果您使用默认消息处理程序,此函数将中止以创建核心转储。
您可以安装自己的处理程序,它将继续运行程序。所以理论上分析仪是对的。可能会发生空指针的潜在取消引用。
我们不是理论家而是实践者,我们意识到这个代码应该被认为是正确的。分析器不熟悉这种代码视图,其中Q_ASSERT
使用了宏。我们将修改分析器,以便它开始将此类代码模式视为正确的。即在未来分析器将假设这里:
Q_ASSERT(_parent);
Q_ASSERT(row < _parent->childCount());
_parent->childCount()
_parent
如果指针等于 nullptr ,则永远不会执行函数调用。如果指针为空,则程序将因调用而提前停止工作qFatal()
。
当然,正如我上面已经说过的,你可以改变处理程序的行为,它不会导致程序中止。但是,在实践中,没有人会更改处理程序并编写我们正在考虑的此类代码。
这可能是答案的终点。所以,我们将改进分析器,仅此而已。然而,不可能预见所有可能的选择。如果是我们自己的宏,如何抑制警告?
假设这个自制的错误记录系统和分析器对自定义函数一无所知Foo()
。
void Foo(bool expr);
#define Q_ASSERT(expr) Foo(expr);
inline T *sibling(int row) const
{
Q_ASSERT(_parent);
Q_ASSERT(row < _parent->childCount())
return _parent ? _parent->child(row) : nullptr;
}
最简单但不是最好的方法是使用注释将警告显式标记为错误:
Q_ASSERT(row < _parent->childCount()) //-V595
另一种选择是改变写代码的风格,写成如下:
inline T *sibling(int row) const
{
if (_parent == nullptr)
{
Q_ASSERT(false);
return nullptr;
}
Q_ASSERT(row < _parent->childCount());
return _parent->child(row);
}
对于此类代码,分析器不会发出警告 V595,因为没有理由这样做。代码变得更长了,但在我看来,它现在在逻辑上更正确和安全。我推荐这种处理此类警告的方式。
最后是在宏中使用警告抑制机制。要在定义宏的头文件中执行此操作,您应该编写注释:
//-V:Q_ASSERT:595
在此之后警告将消失。当然,并不总是可以更改声明宏的文件。然后,您可以使用其中一个全局文件。在 Visual C++ 项目中,一个很好的候选者是stdafx.h。另一种选择是使用诊断配置文件 (pvsconfig)。所有这些方法都在“抑制误报”部分的文档中进行了详细描述。标记库也存在。