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

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

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); } 

我有一个函数,扫描完整的行,并通过链接列表块删除相关的和重新分配 – >下一个指针。

 SingleBlock *deleteBlock; SingleBlock *tempBlock; tempBlock = deleteBlock->next; delete deleteBlock; 

游戏的作品,块被正确删除,一切function,因为它应该是。 然而,在检查时,我仍然可以访问已删除数据的随机位。

如果我打印每个被删除的单块“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 

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

对不起,如果这是有点长啰。

能够访问超出预期的坟墓数据吗?

这在技术上被称为未定义的行为。 如果它能为你提供一jar啤酒,也不要感到惊讶。

能够访问超出预期的坟墓数据吗?

在大多数情况下,是的。 调用删除不会清零内存。

请注意,行为未定义。 使用某些编译器,内存可能被归零。 当你调用delete的时候,会发生内存被标记为可用的情况,所以下次有人做新的时候 ,可以使用内存。

如果你仔细想想,这是合乎逻辑的 – 当你告诉编译器你不再对内存感兴趣时(使用delete ),计算机为什么要花费时间对它进行调零。

这就是C ++调用未定义的行为 – 您可能能够访问数据,您可能不会。 无论如何,这是不对的。

删除不会删除任何内容 – 只是将内存标记为“可以重复使用”。 直到其他分配呼叫储备,并填充该空间,它会有旧的数据。 但是,依靠这个是一个很大的禁忌,基本上如果你删除了一些忘记的东西。

在这方面,在图书馆经常遇到的一个做法是删除function:

 template< class T > void Delete( T*& pointer ) { delete pointer; pointer = NULL; } 

这可以防止我们意外地访问无效的内存。

请注意,调用delete NULL;是完全可以的delete NULL;

堆内存就像一堆黑板。 想象一下,你是一名教师。 当你在教课时,黑板属于你,你可以做任何你想做的事情。 你可以随意涂写,并覆盖你想要的东西。

当课程结束,你即将离开房间,没有任何政策要求你擦黑板 – 你只需把黑板交给下一位老师,他通常能够看到你写下的内容。

当您通过delete()释放内存时,系统不会清除内存。 因此内容仍然可以访问,直到内存被分配用于重新使用和覆盖。

删除释放内存,但不修改它或将其归零。 你仍然不应该访问释放内存。

删除一个对象后,它没有定义它将占用的内存内容会发生什么。 这意味着内存可以被重用,但是实现并不需要重写原来的数据,也不需要立即重用内存。

在对象不存在之后,你不应该访问内存,但不应该让人惊讶的是,有些数据仍然在那里。

这将导致未定义的行为,并删除释放内存,它不重新初始化为零。

如果你想把它归零,那么做:

 SingleBlock::~SingleBlock() { x = y = 0 ; } 

尽pipe运行时可能不会报告这个错误,但是使用适当的错误检查运行时(如Valgrind)会在释放内存后提醒您使用内存。

我build议如果你用new / delete和raw指针(而不是std::make_shared()和类似的)编写代码,那么你在Valgrind下进行unit testing,至less有机会发现这样的错误。

好吧,我也一直在想这个问题,而且我也尝试过一些testing,以便更好地理解底下发生了什么。 标准答案是,在你调用delete之后,你不应该期待访问这个内存点的好处。 不过,这对我来说还不够。 调用delete(ptr)时发生了什么? 这是我find的。 我在Ubuntu 16.04上使用g ++,所以这可能会在结果中起作用。

我在使用delete操作符时第一次预期的是,释放的内存将被交还给系统用于其他进程。 让我说在我尝试过的任何情况都不会发生

使用删除发布的内存似乎仍然被分配给它首先分配给它的程序。 我试过了,调用delete后没有减less内存使用量。 我有一个软件,通过新的通话分配了大约30MB的列表,然后用随后的删除呼叫释放它们。 发生的事情是,在程序运行时看着系统监视器,甚至在删除调用之后长时间的睡眠,我的程序的内存消耗是相同的。 没有减less! 这意味着删除不会释放内存到系统。

实际上,程序分配的内存看起来就是他的永远! 但是,重要的是,如果释放,内存可以再次使用相同的程序,而不必分配任何更多。 我试图分配15MB,释放它们,然后再分配另外15MB的数据,程序从未使用过30MB。 系统监视器总是显示在15MB左右。 就以前的testing而言,我所做的只是改变发生的顺序:半分配,半分配,另一半分配。

所以, 显然程序使用的内存可以增加,但不会缩小 。 我认为也许内存在真实情况下会被释放用于其他进程,比如当没有更多的内存时。 毕竟,当其他进程需要时,让程序永远保存自己的记忆有什么意义? 所以我再次分配了30MB, 在释放它们的同时,我运行了一个memtester ,尽可能多的物理内存。 我期望看到我的软件把内存交给memtester。 但是猜猜它并没有发生!

我已经做了一个简短的截屏,显示了在行动的事情:

删除示例内存

为了100%诚实,发生了一些事情。 当我在程序的释放过程中尝试超过可用物理内存的memtester时,程序使用的内存降到了3MB左右。 memtester进程自动被杀死了,发生的事情更令人惊讶! 我的程序的内存使用量随着每个删除调用而增加! 就好像Ubuntu在memtester事件之后恢复了所有的内存一样。

取自http://www.thecrowned.org/c-delete-operator-really-frees-memory

它不会零/改变内存,但在某些时候,地毯将被从你的脚下拉出。

不,它肯定是不可预测的:这取决于内存分配/释放的速度有多快。

是的,有时可以预料。 而new的数据保留空间, delete只是使用new的指针无效,允许数据写在以前保留的位置; 它不一定删除数据。 但是,您不应该依赖这种行为,因为这些位置的数据可能随时发生变化,可能会导致您的程序行为失常。 这就是为什么在你使用delete指针(或delete[]分配new[] )的数组之后,你应该给它分配NULL,所以你不能篡改一个无效的指针,假设你不会分配内存再次使用该指针之前使用newnew[]