9

我们什么时候需要对 C++ 中的指针使用“assert”,什么时候使用它们,它们最常见的实现方式是什么?

4

7 回答 7

15

通常,您会使用断言来检查一个条件,如果该条件为假,则表明您的应用程序中存在错误。因此,如果在应用程序中的某个时刻不应该遇到NULL 指针,除非存在错误,然后断言它。如果由于某些无效输入而可能遇到它,那么您需要进行适当的错误处理。

于 2009-12-10T00:13:00.850 回答
8

您根本不需要在指针上使用断言。这个想法是为了确保在指针为空时取消引用指针时不会崩溃。

您可以这样做,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)声明你不允许接收空指针,你仍然应该优雅地处理它们。一句老话:对你给予的东西要保守,对你接受的东西要自由(意译)。

于 2009-12-10T00:15:42.670 回答
3

ASSERT 语句作为“强制文档”非常棒——也就是说,它们告诉读者一些关于代码的事情(“这不应该发生”),然后通过让你知道它们是否不成立来强制执行它。

如果这是可能发生的事情(无效输入,无法分配内存),那不是使用 ASSERT 的时候。断言适用于如果每个人都遵守先决条件等不可能发生的事情。

你可以这样做:

ASSERT(pMyPointer);
于 2009-12-10T00:21:52.580 回答
2

根据经验,如果您断言在正常条件下永远不应该发生的空条件,那么您的程序就处于非常糟糕的状态。从这种空状态中恢复很可能会掩盖原始问题。

除非您在编码时考虑到异常保证(linky),否则我说让它崩溃,那么您就知道您遇到了问题。

于 2009-12-10T00:38:12.507 回答
0

我会使用一个 ASSERT,其中空指针不会立即导致崩溃,但可能会导致以后很难发现的错误。
例如:

ASSERT(p);   
strcpy(p, "hello");

有点不必要,它只是用一个致命的断言替换了一个致命的异常!
但是在更复杂的代码中,特别是像智能指针这样的东西,知道检查指针是否就是你想要的东西可能很有用。

请记住,ASSERT 仅在调试版本中运行,它们在发布中消失。

于 2009-12-10T00:28:54.240 回答
0

在C语言中,也有assert函数..在调试模式下,如果assert(x),x条件为假,会弹出一个警报...但是记住它只在调试模式下有效...在发布模式下,所有断言功能全部跳过

于 2009-12-10T07:14:30.927 回答
0

断言用于定义程序应如何运行。话虽如此,在处理指针时最常见的 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 时,我发誓他们为我节省了很多调试时间,甚至知道存在缺陷。

于 2020-01-12T10:16:44.367 回答