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

发布于 2024-08-15 13:56:10 字数 1716 浏览 10 评论 0原文

我编写了一个简单、有效的俄罗斯方块游戏,其中每个块都作为 singleblock 类的实例。

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

我有一个函数可以扫描完整的行,并遍历块的链接列表,删除相关的块并重新分配 -> next 指针。

SingleBlock *deleteBlock;
SingleBlock *tempBlock;

tempBlock = deleteBlock->next;
delete deleteBlock;

游戏正常运行,块被正确删除,一切都按预期运行。然而,经过检查,我仍然可以访问已删除数据的随机位。

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

能够从坟墓之外访问数据吗?

抱歉,如果这有点啰嗦。

I have written a simple, working tetris game with each block as an instance of a class singleblock.

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

I have a function that scans for a complete line, and runs through the linked list of blocks deleting the relevant ones and reassigning the ->next pointers.

SingleBlock *deleteBlock;
SingleBlock *tempBlock;

tempBlock = deleteBlock->next;
delete deleteBlock;

The game works, blocks are deleted correctly and everything functions as it is supposed to. However on inspection I can still access random bits of deleted data.

If I printf each of the deleted singleblocks "x" values AFTER their deletion, some of them return random garbage (confirming the deletion) and some of them return 222, telling me even though the destructor was called the data wasn't actually deleted from the heap. Many identical trials show it is always the same specific blocks that are not deleted properly.

The results:

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

Is being able to access data from beyond the grave expected?

Sorry if this is a bit long winded.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

耳钉梦 2024-08-22 13:56:10

是否能够从坟墓之外访问数据?

从技术上讲,这称为未定义行为。如果它为您提供一罐啤酒,也不要感到惊讶。

Is being able to access data from beyond the grave expected?

This is technically known as Undefined Behavior. Don't be surprised if it offers you a can of beer either.

猥琐帝 2024-08-22 13:56:10

是否能够从坟墓之外访问数据?

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

请注意,该行为未定义。使用某些编译器,内存可能会被清零。当您调用delete时,会发生的情况是内存被标记为可用,因此下次有人执行new时,内存可能会被使用。

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

Is being able to access data from beyond the grave expected?

In most cases, yes. Calling delete doesn't zero the memory.

Note that the behavior is not defined. Using certain compilers, the memory may be zeroed. When you call delete, what happens is that the memory is marked as available, so the next time someone does new, the memory may be used.

If you think about it, it's logical - when you tell the compiler that you are no longer interested in the memory (using delete), why should the computer spend time on zeroing it.

慵挽 2024-08-22 13:56:10

删除不会删除任何内容——它只是将内存标记为“可以自由重用”。在其他一些分配调用保留并填充该空间之前,它将拥有旧数据。然而,依赖它是一个很大的禁忌,基本上,如果你删除了某些东西,就忘记它。

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

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

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

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

Delete doesn't delete anything -- it just marks the memory as "being free for reuse". Until some other allocation call reserves and fills that space it will have the old data. However, relying on that is a big no-no, basically if you delete something forget about it.

One of the practices in this regard that is often encountered in libraries is a Delete function:

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

This prevents us from accidentally accessing invalid memory.

Note that it is perfectly okay to call delete NULL;.

天煞孤星 2024-08-22 13:56:10

这就是 C++ 所说的未定义行为 - 您可能能够访问数据,也可能不能。无论如何,这是错误的做法。

It is what C++ calls undefined behaviour - you might be able to access the data, you might not. In any case, it is the wrong thing to do.

避讳 2024-08-22 13:56:10

堆内存就像一堆黑板。想象你是一名老师。当你教课时,黑板是你的,你可以用它做任何你想做的事。您可以在上面乱涂乱画并根据需要覆盖内容。

当课程结束并且你准备离开教室时,没有任何政策要求你擦掉黑板——你只需将黑板交给下一位老师,他通常能够看到你写下的内容。

Heap memory is like a bunch of blackboards. Imagine you are a teacher. While you're teaching your class, the blackboard belongs to you, and you can do whatever you want to do with it. You can scribble on it and overwrite stuff as you wish.

When the class is over and you are about to leave the room, there is no policy that requires you to erase the blackboard -- you simply hand the blackboard off to the next teacher who will generally be able to see what you wrote down.

星軌x 2024-08-22 13:56:10

删除一个对象后,并没有定义它所占用的内存内容会发生什么。这确实意味着内存可以自由地重新使用,但实现不必覆盖原来存在的数据,也不必立即重新使用内存。

对象消失后,您不应该访问内存,但一些数据完好无损地保留在那里也就不足为奇了。

After deleting an object it's not defined what will happen to the contents of the memory that it occupied. It does mean that that memory is free to be re-used, but the implementation doesn't have to overwrite the data that was there originally and it doesn't have to reuse the memory immediately.

You shouldn't access the memory after the object is gone but it shouldn't be surpising that some data remains in tact there.

青瓷清茶倾城歌 2024-08-22 13:56:10

当您通过delete()释放内存时,系统不会清除内存。因此,在分配内存以供重用和覆盖之前,内容仍然可以访问。

The system does not clear the memory when you release it via delete(). The contents are therefore still accessible until the memory is assigned for reuse and overwritten.

风铃鹿 2024-08-22 13:56:10

删除会释放内存,但不会修改它或将其清零。尽管如此,您不应该访问已释放的内存。

delete deallocates the memory, but does not modify it or zero it out. Still you should not access deallocated memory.

全部不再 2024-08-22 13:56:10

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

Yes, it can be expected at times. Whereas new reserves space for data, delete simply invalidates a pointer created with new, allowing data to be written at the previously reserved locations; it doesn't necessarily delete the data. However, you shouldn't rely on that behaviour because the data at those locations could change at any time, possibly causing your program to misbehave. This is why after you use delete on a pointer (or delete[] on an array allocated with new[]), you should assign NULL to it so that you can't tamper with an invalid pointer, assuming you won't allocate memory using new or new[] before using that pointer again.

东北女汉子 2024-08-22 13:56:10

它还不会将记忆归零/改变……但在某些时候,地毯将会从你脚下被拉走。

不,这当然是不可预测的:这取决于内存分配/释放的速度。

It won't zero/change memory just yet... but at some point, the rug is going to be pulled from under your feet.

No it is certainly not predictable: it depends on how fast memory allocation/deallocation is churned.

哭泣的笑容 2024-08-22 13:56:10

尽管您的运行时可能不会报告此错误,但使用正确的错误检查运行时(例如 Valgrind)会在释放内存后提醒您内存的使用情况。

我建议,如果您使用 new/delete 和原始指针(而不是 std::make_shared() 等)编写代码,那么您在 Valgrind 下进行单元测试至少有机会发现此类错误。

Although it's possible that your runtime doesn't report this error, using a proper error-checking runtime such as Valgrind will alert you to the use of memory after it has been freed.

I recommend that if you write code with new/delete and raw pointers (rather than std::make_shared() and similar), that you exercise your unit tests under Valgrind to at least have a chance of spotting such errors.

兮子 2024-08-22 13:56:10

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

如果你想让它归零,那么:

SingleBlock::~SingleBlock()

{    x = y = 0 ; }

It will lead to undefined behaviour and delete deallocates memory , it does not reinitialize it with zero .

If you want to make it zero out then do :

SingleBlock::~SingleBlock()

{    x = y = 0 ; }
娇纵 2024-08-22 13:56:10

好吧,我也想知道这个问题有一段时间了,并且我尝试运行一些测试以更好地了解幕后发生的事情。标准答案是,在调用delete之后,您不应该期望访问该内存点有任何好处。
然而,这对我来说似乎还不够。调用delete(ptr)时到底发生了什么?这是我发现的。我在 Ubuntu 16.04 上使用 g++,因此这可能会对结果产生影响。

当使用删除运算符时,我首先期望的是释放的内存将被交还给系统以供其他进程使用。让我说在我尝试过的任何情况下这种情况都不会发生

delete释放的内存似乎仍然分配给它首先用new分配的程序。我已经尝试过,调用delete后内存使用量没有减少。我有一个软件,它通过 new 调用分配了大约 30MB 的列表,然后通过后续的 delete 调用释放它们。发生的情况是,在程序运行时查看系统监视器,即使在删除调用后长时间睡眠,程序的内存消耗也是相同的。没有减少!这意味着删除不会释放系统内存。

事实上,看起来程序分配的内存永远是他的了!然而,重点是,如果释放内存,同一程序可以再次使用内存,而无需再分配。我尝试分配 15MB,释放它们,然后再分配另外 15MB 数据,但程序从未使用过 30MB。系统监视器始终显示 15MB 左右。对于之前的测试,我所做的只是改变事情发生的顺序:一半分配,一半释放,另一半分配。

因此,显然程序使用的内存可以增加,但永远不会减少。我认为在关键情况下,例如当没有更多可用内存时,内存可能真的会被释放给其他进程。毕竟,当其他进程需要它时,让一个程序永远保留自己的内存有什么意义呢?因此,我再次分配了 30MB,并且在释放它们时我使用尽可能多的物理内存运行 memtester。我希望看到我的软件将其内存交给 memtester。但你猜猜,这并没有发生!

我制作了一个简短的截屏视频,展示了实际情况:

删除示例内存

百分百诚实地说,在某种情况下发生了某些事情。当我在程序的释放过程中尝试使用超过可用物理内存的 memtester 时,程序使用的内存下降到 3MB 左右。然而 memtester 进程被自动终止了,而发生的事情更令人惊讶!我的程序的内存使用量随着每次删除调用而增加!就好像 Ubuntu 在 memtester 事件后恢复了所有内存一样。

摘自
http://www.thecrowned.org/c-delete-operator-真正释放内存

Well, I have been wondering about this for quite a while as well, and I have tried to run some tests to better understand what's going on under the hood. The standard answer is that after you call delete you should not expect anything good from accessing that memory spot.
However, this did not seem enough to me. What is it really happening when calling delete(ptr)? Here's what I've found. I'm using g++ on Ubuntu 16.04, so this may play a role in the results.

What I first expected when using the delete operator was that the freed memory would be handed back to the system for usage in other processes. Let me say this does not happen under any of the circumstances I have tried.

Memory released with delete still seem to be allocated to the program it first allocated it with new. I have tried, and there is no memory usage decrease after calling delete. I had a software which allcated around 30MB of lists through new calls, and then released them with subsequent delete calls. What happened is that, looking at the System monitor while the program was running, even a long sleep after the delete calls, memory consumption my the program was the same. No decrease! This means that delete does not release memory to the system.

In fact, it looks like memory allocated by a program is his forever! However, the point is that, if deallocated, memory can be used again by the same program without having to allocate any more. I tried to allocate 15MB, freeing them, and then allocating another 15MB of data after, and the program never used 30MB. System monitor always showed it around 15MB. What I did, in respect to the previous test, was just to change the order in which things happened: half allocation, half deallocation, other half of allocation.

So, apparently memory used by a program can increase, but never shrink. I thought that maybe memory would really be released for other processes in critical situations, such as when there is no more memory available. After all, what sense would it make to let a program keep its own memory forever, when other processes are asking for it? So I allocated the 30MB again, and while deallocating them I run a memtester with as much physical memory I could. I expected to see my software hand out its memory to memtester. But guess it, it did not happen!

I've made up a short screencast that shows the thing in action:

Delete example memory

To be 100% honest, there was a situation in which something happened. When I tried memtester with more than the available physical memory in the middle of the deallocation process of my program, the memory used by my program dropped to around 3MB. The memtester process was killed automatically though, and what happened was even more surprising! The memory usage of my program increased with each delete call! It was just as if Ubuntu was restoring all its memory back after the memtester incident.

Taken from
http://www.thecrowned.org/c-delete-operator-really-frees-memory

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文