简体   繁体   English

为什么删除不破坏任何东西?

[英]Why doesn't delete destroy anything?

I'm playing a little with memory dynamic allocation, but I don't get a point.我在玩一点内存动态分配,但我没有得到一点。 When allocating some memory with the new statement, I'm supposed to be able to destroy the memory the pointer points to using delete .当使用new语句分配一些内存时,我应该能够使用delete销毁指针指向的内存。

But when I try, this delete command doesn't seem to work since the space the pointer is pointing at doesn't seem to have been emptied.但是当我尝试时,这个delete命令似乎不起作用,因为指针指向的空间似乎没有被清空。

Let's take this truly basic piece of code as an example:让我们以这段真正基本的代码为例:

#include <iostream>  

using namespace std;

int main()  
{  
    //I create a pointer-to-integer pTest, make it point to some new space,  
    // and fulfill this free space with a number;  
    int* pTest;  
    pTest = new int;  
    *(pTest) = 3;  
    cout << *(pTest) << endl; 

    // things are working well so far. Let's destroy this
    // dynamically allocated space!
    delete pTest;

    //OK, now I guess the data pTest pointed to has been destroyed 
    cout << *(pTest) << endl; // Oh... Well, I was mistaking.  

    return 0;  
}  

Any clue ?任何线索?

It's time to learn what undefined behavior is.是时候了解什么是未定义行为了。 :) :)

In C++, when you do something illegal/nonsensical/bad/etc.在 C++ 中,当你做一些非法/无意义/坏/等的事情时。 the standard often says that "it leads to undefined behavior."该标准经常说“它会导致未定义的行为”。 This means that from that point forward, the state of your program is completely non-guaranteed, and anything could happen.这意味着从那时起,您的程序的状态是完全无法保证的,任何事情都可能发生。

At the point where you do your last *(pTest) , you get undefined behavior.在您执行最后一次*(pTest) ,您会得到未定义的行为。 This is because pTest does not point to a valid object, and dereferencing such a pointer is undefined.这是因为pTest不指向有效对象,并且取消引用这样的指针是未定义的。 So what you're seeing is totally allowed: undefined output.所以你看到的是完全允许的:未定义的输出。

All you've done is said "I'm finished with this allocation."你所做的只是说“我已经完成了这个分配。” Once you've said that, you shouldn't (and indeed, cannot) inspect or care about that memory any longer.一旦你说过,你不应该(事实上,不能)再检查或关心那个记忆。 It doesn't even make conceptual sense to deallocate something then try to use it;释放某些东西然后尝试使用它甚至没有概念上的意义; you've said you were done!你说你完成了!

Your output is somewhat predictable though: likely, your OS simply says "okay, thanks for the memory" and that's it.不过,您的输出在某种程度上是可预测的:很可能,您的操作系统只是说“好的,感谢您提供内存”,仅此而已。 It has no reason to actually "reset" the memory, or do anything special.它没有理由真正“重置”内存,或做任何特别的事情。 That would indeed be a waste of time, when nobody (including your own program) is not using it.这确实是在浪费时间,因为没有人(包括您自己的程序)不使用它。

But remember, this output is completely undefined.但请记住,这个输出是完全未定义的。 Don't try to use objects that don't exist.不要尝试使用不存在的对象。 Perhaps a better test would have been:也许更好的测试是:

#include <iostream>

struct foo
{
    ~foo()
    {
        std::cout << "foo is gone :(" << std::endl;
    }
};

int main(void)
{
    foo* f = new foo();
    delete f; // you'll see that the object is destroyed.
}

Although it seems you were looking to see what happens with the memory itself.尽管您似乎想看看内存本身会发生什么。 Just remember that it makes no sense to get rid of memory then try to use it, so the answer is: who knows.请记住,摆脱内存然后尝试使用它是没有意义的,所以答案是:谁知道。 It's up to your specific platform, which C++ doesn't care about.这取决于您的特定平台,C++ 不关心。

Calling delete will mark the memory area as free.调用 delete 会将内存区域标记为空闲。 It won't necessary reset its old value.它不需要重置其旧值。

You are advised to set your pointer to 0, after calling delete:建议您在调用 delete 后将指针设置为 0:

delete pTest;
pTest = 0;

delete operator calls the destructor of the object and deallocates the memory previously allocated to the object. delete 运算符调用对象的析构函数并释放先前分配给对象的内存。 It doesn't affect the pointer variable that points to the deleted object.它不会影响指向已删除对象的指针变量。

So when dereferencing a pointer pointing to a destroyed object, you'll get trouble.因此,当取消引用指向已销毁对象的指针时,您会遇到麻烦。

The answer is performance .答案是性能

It's a great debug aid to fill all freed memory with an invalid value ( 0xCCCCCCCC , 0xDEADDEAD , etc.) to catch attempts to use stale pointers to already-freed memory.用无效值( 0xCCCCCCCC0xDEADDEAD等)填充所有已释放的内存以捕获使用指向已释放内存的陈旧指针的尝试,这是一个很好的调试辅助工具。

But modifying a freed memory costs CPU time, so for performance reasons, the OS will just add the freed memory block to its "free" list, and leave the contents intact.但是修改释放的内存会消耗 CPU 时间,因此出于性能原因,操作系统只会将释放的内存块添加到其“空闲”列表中,并保持内容不变。

Dereferencing a pointer that points to deallocated memory is undefined behavior.取消引用指向已释放内存的指针是未定义的行为。

Many times it will just work, because the memory provided by new is usually part of a bigger chunk of allocated memory that the allocator manages.很多时候它会正常工作,因为new提供的内存通常是分配器管理的更大的已分配内存块的一部分。 When you call delete , it will call the relevant destructors and mark the memory as free, which usually means "ready for reuse".当你调用delete ,它会调用相关的析构函数并将内存标记为空闲,这通常意味着“准备重用”。 So, looking in that memory you'll find the same data that was there before the call to delete , or some other data if that chunk of memory has been reassigned after a new call.因此,查看该内存,您会发现在调用delete之前存在的相同数据,或者如果该内存块在new调用后已重新分配,则会找到一些其他数据。

Note that nothing forbids that the new / delete allocator works as a thin wrapper around the OS virtual memory functions, so when all the allocated blocks relative to a page has been deallocated, the whole page is freed and any attempt to access it results in an address violation.请注意,没有禁止new / delete分配器作为 OS 虚拟内存功能的瘦包装器工作,因此当与页面相关的所有已分配块都已被释放时,整个页面将被释放并且任何访问它的尝试都会导致地址违规。

TL,DR version: don't deference pointers which point to deallocated memory: it may work sometimes, sometimes will give you back garbage, sometimes it will trigger an access violation. TL,DR 版本:不要尊重指向已释放内存的指针:它有时可能会起作用,有时会返回垃圾,有时会触发访问冲突。

A good way to notice immediately if you're doing this kind of mistake is to set your pointers to NULL after deleting the memory they point to: if your code tries to dereference a NULL pointer, on almost any system this will make the application crash, so faults like these won't go unnoticed.如果您正在犯这种错误,立即注意的一个好方法是在删除它们指向的内存后将指针设置为 NULL:如果您的代码试图取消引用 NULL 指针,则几乎在任何系统上这都会使应用程序崩溃,因此此类故障不会被忽视。

It could have referred to any piece of mapped memory.它可以引用任何一块映射内存。 Or maybe unmapped memory, depending upon how long your program has been executing, details of memory allocations, and if the libraries are returning memory to the OS later on...或者可能是未映射的内存,这取决于您的程序执行了多长时间、内存分配的详细信息,以及这些库稍后是否将内存返回给操作系统......

If delete actually cleared all the memory that is being deleted, programs would spend a significantly longer time running, because they'd waste a lot of time scrubbing memory that will probably be overwritten sooner or later anyway.如果delete实际上清除了所有被删除的内存,程序将花费更长的时间运行,因为它们会浪费大量时间清理可能迟早会被覆盖的内存。 It might be good for debugging, but in production use, there's just not much call for actually scrubbing memory contents.它可能有利于调试,但在生产使用中,实际清理内存内容的要求并不多。 (Crypto keys are a good exception, of course; scrubbing those before calling delete or free is a good idea.) (当然,加密密钥是一个很好的例外;在调用deletefree之前delete是个好主意。)

What would destroying the data mean?销毁数据意味着什么? I suppose it could zero it out, but why bother?我想它可以将其归零,但何必呢? It's assumed dirty when we get it from the environment, so why clean it before we return it?当我们从环境中得到它时,它被认为是脏的,那么为什么在我们返回它之前清洁它呢? We don't care what's in it, because we are relinquishing our right to read it.我们不在乎里面有什么,因为我们正在放弃阅读它的权利。 And as to why delete doesn't zero out the pointer itself:至于为什么 delete 不会将指针本身清零:

http://www2.research.att.com/~bs/bs_faq2.html#delete-zero http://www2.research.att.com/~bs/bs_faq2.html#delete-zero

Just a simple example to illustrate what might happen, and what the undefined behaviour that some people mentioned means.只是一个简单的例子来说明可能会发生什么,以及一些人提到的未定义行为意味着什么。

If we add two extra lines of code before the print:如果我们在打印之前添加两行额外的代码:

delete pTest;

int *foo = new int;
*foo = 42;

cout << *pTest << endl;

The printed value of pTest could very well be 3, as it was in your case. pTest 的打印值很可能是 3,就像您的情况一样。 However, the printed value could also be 42. As the pTest pointer was deleted, its memory was freed.然而,打印的值也可能是 42。当 pTest 指针被删除时,它的内存也被释放了。 Because of this, it is possible that the foo pointer will point to the same location in memory that pTest used to point to before it was deleted.因此,foo 指针可能会指向内存中 pTest 在删除之前所指向的相同位置。

Calling delete for a pointer marks the memory block pointed by it to 'free memory'.对指针调用 delete 会将其指向的内存块标记为“空闲内存”。 It doesn't destroy the items in the memory right away.它不会立即销毁内存中的项目。 The 'items' get destroyed only after the process has returned given that 'delete' was used for the pointer pointing to that memory block. 'items' 只有在进程返回后才会被销毁,因为'delete' 被用于指向该内存块的指针。 In case 'delete' isn't used, it stays there forever resulting in a memory leak.如果不使用“删除”,它会永远留在那里导致内存泄漏。

To solve your problem, just:要解决您的问题,只需:

delete pTest;
pTest=NULL;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM