C++ delete - 删除我的对象,但我仍然可以访问数据?

Ash*_*Ash 30 c++ pointers c++-faq undefined-behavior

我写了一个简单的,工作的俄罗斯方块游戏,每个块作为类单块的一个实例.

class SingleBlock
{
    public:
    SingleBlock(int, int);
    ~SingleBlock();

    int x;
    int y;
    SingleBlock *next;
};

class MultiBlock
{
    public:
    MultiBlock(int, int);

    SingleBlock *c, *d, *e, *f;
};

SingleBlock::SingleBlock(int a, int b)
{
    x = a;
    y = b;
}

SingleBlock::~SingleBlock()
{
    x = 222;
}

MultiBlock::MultiBlock(int a, int b)
{
    c = new SingleBlock (a,b);
    d = c->next = new SingleBlock (a+10,b);
    e = d->next = new SingleBlock (a+20,b);
    f = e->next = new SingleBlock (a+30,b);
}
Run Code Online (Sandbox Code Playgroud)

我有一个扫描完整行的函数,并运行删除相关的块的链接列表并重新分配 - >下一个指针.

SingleBlock *deleteBlock;
SingleBlock *tempBlock;

tempBlock = deleteBlock->next;
delete deleteBlock;
Run Code Online (Sandbox Code Playgroud)

游戏工作,块被正确删除,一切都按照预期运行.但是在检查时我仍然可以访问已删除数据的随机位.

如果我删除它们后删除了每个删除的单个块"x"值,其中一些返回随机垃圾(确认删除),其中一些返回222,告诉我即使析构函数被调用,数据实际上并未从中删除堆.许多相同的试验表明它总是与未正确删除的相同特定块相同.

结果:

Existing Blocks:
Block: 00E927A8
Block: 00E94290
Block: 00E942B0
Block: 00E942D0
Block: 00E942F0
Block: 00E94500
Block: 00E94520
Block: 00E94540
Block: 00E94560
Block: 00E945B0
Block: 00E945D0
Block: 00E945F0
Block: 00E94610
Block: 00E94660
Block: 00E94680
Block: 00E946A0

Deleting Blocks:
Deleting ... 00E942B0, X = 15288000
Deleting ... 00E942D0, X = 15286960
Deleting ... 00E94520, X = 15286992
Deleting ... 00E94540, X = 15270296
Deleting ... 00E94560, X = 222
Deleting ... 00E945D0, X = 15270296
Deleting ... 00E945F0, X = 222
Deleting ... 00E94610, X = 222
Deleting ... 00E94660, X = 15270296
Deleting ... 00E94680, X = 222
Run Code Online (Sandbox Code Playgroud)

是否能够访问超出预期的数据?

对不起,如果这有点长啰嗦.

dir*_*tly 78

是否能够访问超出预期的数据?

这在技术上称为未定义行为.如果能为您提供一罐啤酒,请不要感到惊讶.

  • 此外,最好添加一个事实的推论...如果有一个"敏感"的数据存储在内存中,应该认为在删除它之前完全覆盖它是一个好习惯(为了防止其他段访问它的代码). (11认同)
  • @dirkgently:是的,我认为析构函数是正确的地方.你不想太快这么做,你不能太晚做. (3认同)

Mar*_*tin 31

是否能够访问超出预期的数据?

在大多数情况下,是的.调用delete不会使内存归零.

请注意,未定义行为.使用某些编译器,可以将存储器归零.当您调用delete时,会发生内存被标记为可用,因此下次有人新建时,可能会使用内存.

如果您考虑一下,这是合乎逻辑的 - 当您告诉编译器您不再对内存感兴趣时(使用删除),为什么计算机会花时间将其归零.

  • @Thomas Matthews我并不是说尝试访问它是一个好主意.@Curt Nichols那是在玩文字.根据您使用的编译器,您可以*期望*在调用delete时内存不会立即归零.你显然不能确定它. (5认同)

小智 9

这是C++调用未定义的行为 - 您可能能够访问数据,您可能不会.无论如何,这是错误的做法.


Kor*_*icz 9

删除不会删除任何内容 - 它只是将内存标记为"可以重复使用".在某些其他分配呼叫预留并填充该空间之前,它将具有旧数据.然而,依靠这是一个很大的禁忌,基本上如果你删除了忘记它的东西.

在库中经常遇到的这方面的一种做法是删除功能:

template< class T > void Delete( T*& pointer )
{
    delete pointer;
    pointer = NULL;
}
Run Code Online (Sandbox Code Playgroud)

这可以防止我们意外访问无效内存.

请注意,打电话是完全可以的delete NULL;.

  • @Mark在删除后将指针设置为NULL不是C++中的通用优良做法.有时候它是一件好事,有时它是毫无意义的,可以隐藏错误. (11认同)
  • "这可以防止我们意外访问无效内存".这不是真的,它证明了为什么使用这个技巧应该与编写错误的代码相关联.`char*ptr = new char; char*ptr2 = ptr; 删除(PTR);*ptr2 = 0;`.我不小心访问了无效的内存.它只是混乱,想要取消*引用*,因为它相信这可以保护被引用的*对象*.另外,不要忘记您需要单独版本的此函数来指向数组. (6认同)
  • 我讨厌这种做法.它非常混乱,而且...... (4认同)
  • @Kornel 任何使用这种宏的 C++ 库都非常可疑,恕我直言。简而言之,它应该是一个内联模板函数。 (2认同)

小智 6

堆内存就像一堆黑板。想象你是一位老师。当你在教你的课时,黑板是你的,你可以用它做任何你想做的事。您可以在上面随意涂鸦并覆盖内容。

当课程结束并且您将要离开房间时,没有规定要求您擦除黑板——您只需将黑板交给下一位老师,他通常能够看到您写下的内容。