我们什么时候需要对 C++ 中的指针使用“assert”,什么时候使用它们,它们最常见的实现方式是什么?
7 回答
通常,您会使用断言来检查一个条件,如果该条件为假,则表明您的应用程序中存在错误。因此,如果在应用程序中的某个时刻不应该遇到NULL 指针,除非存在错误,然后断言它。如果由于某些无效输入而可能遇到它,那么您需要进行适当的错误处理。
您根本不需要在指针上使用断言。这个想法是为了确保在指针为空时取消引用指针时不会崩溃。
您可以这样做,assert
但这不是一种非常专业的方法来处理此类错误,因为它总是会终止程序 - 例如,如果用户没有保存他们最后三个小时的数据输入,这不是一个好主意。
你应该对指针做的是检查它们是否为空并优雅地失败。换句话说,让你的函数返回某种错误或什么都不做(不是每个人都会同意这种方法,但如果它被记录在案,它是完全可以接受的)。
在我看来,这些assert
东西是为了在开发过程中发现问题,这就是为什么你会发现 assert 在某些编译器下的发布版本中没有任何作用。它不能替代防御性编程。
至于怎么做:
#include <assert.h>
void doSomethingWithPointer (int *p) {
assert (p != 0);
cout << *p << endl;
}
但这会更好:
void doSomethingWithPointer (int *p) {
if (p != 0)
cout << *p << endl;
}
换句话说,即使你的“合同”(API)声明你不允许接收空指针,你仍然应该优雅地处理它们。一句老话:对你给予的东西要保守,对你接受的东西要自由(意译)。
ASSERT 语句作为“强制文档”非常棒——也就是说,它们告诉读者一些关于代码的事情(“这不应该发生”),然后通过让你知道它们是否不成立来强制执行它。
如果这是可能发生的事情(无效输入,无法分配内存),那不是使用 ASSERT 的时候。断言仅适用于如果每个人都遵守先决条件等不可能发生的事情。
你可以这样做:
ASSERT(pMyPointer);
根据经验,如果您断言在正常条件下永远不应该发生的空条件,那么您的程序就处于非常糟糕的状态。从这种空状态中恢复很可能会掩盖原始问题。
除非您在编码时考虑到异常保证(linky),否则我说让它崩溃,那么您就知道您遇到了问题。
我会使用一个 ASSERT,其中空指针不会立即导致崩溃,但可能会导致以后很难发现的错误。
例如:
ASSERT(p);
strcpy(p, "hello");
有点不必要,它只是用一个致命的断言替换了一个致命的异常!
但是在更复杂的代码中,特别是像智能指针这样的东西,知道检查指针是否就是你想要的东西可能很有用。
请记住,ASSERT 仅在调试版本中运行,它们在发布中消失。
在C语言中,也有assert函数..在调试模式下,如果assert(x),x条件为假,会弹出一个警报...但是记住它只在调试模式下有效...在发布模式下,所有断言功能全部跳过
断言用于定义程序应如何运行。话虽如此,在处理指针时最常见的 Assert() 用途要么是它们是有效的(非 NULL 并指向有效内存),要么是它们的内部状态在它们指向对象时是有效的/类实例,例如。
断言不是为了替换或充当错误条件代码,而是为了强制执行您为代码的功能设置的规则,例如在给定的时间点应该有什么条件。
例如,
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;
}
许多 Asserts 用户一旦熟悉了 Assertions 就会选择将其应用于内部状态验证。我们称这些 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();
要记住的重要一点是,这是为了确保当错误确实发生时,这意味着程序无法按您预期的那样工作,然后错误的影响发生在尽可能接近代码中发生的位置,以便使调试更容易。我在自己的代码中广泛使用了 Asserts,在 Microsoft 时,我发誓他们为我节省了很多调试时间,甚至知道存在缺陷。