我们什么时候需要对 C++ 中的指针使用“assert”?何时使用它们,它们最常见的实现方式是什么?
通常,您会使用断言来检查条件,如果条件为假,则表明应用程序中存在错误。因此,如果在应用程序中的某个时刻“不应该”遇到 NULL 指针(除非存在错误),则断言它。如果由于某些无效输入而可能遇到这种情况,那么您需要进行适当的错误处理。
您可以使用 assert
你应该对指针做的是检查它们是否为空(或任何其他有问题的条件)并
优雅地失败。换句话说,让你的函数返回某种错误或什么都不做(不是每个人都会同意这种方法,但如果有记录的话,这是完全可以接受的)。
在我看来,assert
开发
期间捕获问题,这就是为什么你会发现assert在某些编译器下的发布版本中没有任何作用。它不能替代防御性编程。 至于如何做:
#include <assert.h>
void doSomethingWithPointer (int *p) {
assert (p != 0); // does *nothing* if `NDEBUG` set.
cout << *p << endl;
}
但这会更好,比如:
void doSomethingWithPointer (int *p) {
if (p != 0) // protects against null pointer always.
cout << *p << endl;
}
换句话说,即使你的“合约”(API)规定你不允许接收空指针,你仍然应该优雅地处理它们。一句古老的名言:
在你所给予的方面要保守,在你接受的方面要自由(释义)。
ASSERT 语句非常适合作为“强制文档”——也就是说,它们告诉读者有关代码的一些信息(“这永远不应该发生”),然后通过让您知道它们是否不成立来强制执行它。
可能
发生的事情(无效输入、无法分配内存),那就是不使用ASSERT的时候。断言仅适用于如果每个人都遵守先决条件等就不可能发生的事情。 你可以这样做:
ASSERT(pMyPointer);
根据经验,如果您断言空条件,在正常情况下应该“永远不会发生”,那么您的程序处于非常糟糕的状态。从这种空条件中恢复更有可能掩盖原始问题。
linky)我说让它崩溃,那么你就知道你有问题。
有点不必要,它只是用致命断言替换致命异常!
但在更复杂的代码中,特别是像智能指针这样的代码,了解检查指针是否是您想要的可能会很有用。
记住 ASSERT 仅在调试版本中运行,它们在版本中消失。
断言不是为了替换或充当错误条件代码,而是为了强制执行您对代码功能设置的规则,例如在给定时间点应满足哪些条件。
function int f(int x, int * pY)
{
// These are entrance conditions expected in the function. It would be
// a BUG if this happened at all.
Assert(x >= 0);
Assert(pY != nullptr);
Assert(*pY >= 0, "*pY should never be less than zero");
// ...Do a bunch of computations with x and pY and return the result as z...
int z = x * 2 / (x + 1) + pow(*pY, x); // Maybe z should be always positive
// after these calculations:
Assert(x >= 0, "X should always be positive after calculations);
// Maybe *pY should always be non-zero after calculations
Assert(*pY != 0, "y should never be zero after computation");
Assert(z > 0):
return z;
}
许多断言用户在熟悉断言后选择将断言应用于内部状态验证。我们称这些 Invariants() 为类上的方法,这些方法断言有关对象内部的许多事情应该始终保持正确。
例如:
class A
{
public:
A(wchar_t * wszName)
{
_cch = 0;
_wszName = wszName;
}
// Invariant method to be called at times to verify that the
// internal state is consistent. This means here that the
// internal variable tracking the length of the string is
// matching the actual length of the string.
void Invariant()
{
Assert(pwszName != nullptr);
Assert(_cch == wcslen(pwszName));
}
void ProcessABunchOfThings()
{
...
}
protected:
int _cch;
wchar_t * pwszName;
}
// Call to validate internal state of object is consistent/ok
A a(L"Test Object");
a.Invariant();
a.ProcessABunchOfThings();
a.Invariant();
要记住的重要一点是,这是为了确保当错误确实发生时,这意味着程序没有按您预期的方式工作,然后错误的影响尽可能接近代码中发生的位置,以便按顺序发生使调试更容易。我在自己的代码中以及在 Microsoft 期间广泛使用了断言,我对它们发誓,因为它们为我节省了很多调试时间,甚至知道存在缺陷。