0

鉴于这一小段代码:

#include <iostream>
#include <assert.h>
using namespace std;


struct Foo
{
    // something
};


int main()
{

    Foo *p1 = new Foo;
    Foo * p2 = p1;
    assert(NULL != p1);
    delete p1;
    p1 = NULL;

    assert(NULL != p2);
    delete p2;

    cout << "everything is cool!" << endl;

    return 0;
}

当我删除p1时,第二个 assert( assert(NULL != p2);) 没有失败,为什么?

输出 :everything is cool!

那么为什么断言p2没有失败呢?

4

4 回答 4

4

当我删除 p1 时,第二个断言 (assert(NULL != p2);) 没有失败,为什么?

删除p1或分配给它对它本身没有影响p2。在你 delete 之后p1p2仍然指向那个地址,即一个失效的对象。它变成了所谓的悬空指针。当然,访问或删除它(你正在做的)是未定义的行为。

于 2012-08-25T13:24:29.303 回答
2
  1. 尤其是看星星。

    int i;
    
    int *p1 = &i;
    assert(p1 != NULL);
    
    int *p2 = p1;
    assert(p2 != NULL);
    
    *p1 = 10;
    assert(i == 10);
    assert(*p2 == 10);
    
    p1 = NULL; // does not affect the object p1 was pointing at
    
    assert(i == 10);
    assert(*p2 == 10);
    assert(p2 != NULL); // (which we already know, if the previous assert didn't crash)
    
  2. 你怀疑一切都不酷是对的。该程序在同一个对象上调用了两次删除操作符(“双重释放”错误),这会破坏堆。如果程序继续,您会在某个时候看到未定义的行为。具有未定义的行为反而会破坏编写计算机程序的意义。如果您想立即明确地看到这样的错误,请在 valgrind 的 memcheck 或等效项下运行它。

于 2012-08-25T13:52:13.097 回答
1

C++ 中最大和最令初学者困惑的误称之一是术语“删除指针”。这无疑源于delete表达式以指针作为参数的事实:

T * p = new T;  // #1
delete p;       // #2

然而,真正发生的事情是第 1 行创建了一个新的、动态的、未命名的对象。再想一想:没有变量的值是第 1 行中创建的对象。该对象确实遥不可及,因为它确实不在任何范围内。我们所拥有的只是一个指向它的指针。

要结束动态变量的生命周期,我们必须使用delete表达式。但是由于我们已经知道我们只能真正拥有对象的指针,而不是对象本身*,因此表达式方便地接受指向我们正在删除的对象的指针。

所以实际上我们应该说在第 2 行“我们*p通过将指针指向delete表达式来删除对象”(即&*p == p)。

指针本身完全不受delete调用的影响。

*) 是的,我们也可以有一个引用变量,比如T & r = *new T;,但这太疯狂了。

于 2012-08-25T13:49:34.677 回答
0

delete p;不影响p。它销毁p指向的对象并释放其内存。p仍然具有以前的价值。

于 2012-08-25T13:33:16.957 回答