【问题标题】:Why doesn't delete destroy anything?为什么删除不破坏任何东西?
【发布时间】:2011-03-17 20:41:39
【问题描述】:

我正在玩一些内存动态分配,但我不明白。当使用new 语句分配一些内存时,我应该能够使用delete 销毁指针指向的内存。

但是当我尝试时,这个delete 命令似乎不起作用,因为指针指向的空间似乎没有被清空。

让我们以这段真正基本的代码为例:

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

有什么线索吗?

【问题讨论】:

  • 是的,一个菜鸟quieston,但不是一个微不足道的。
  • 标准定义的“破坏”关注对象何时变得无法使用,在运行类作者编写的任何清理代码之后。它不是指清理对象以前占用的内存,类作者/用户没有请求,因此会浪费时间。
  • 如果你想破坏内存,用螺丝刀撬开主板上的芯片。 delete 所做的就是允许将内存分配给其他东西。它可能会或可能不会更改存储在实际位中的值。但是使用指向已删除对象的指针读取这些位会导致 未定义的行为

标签: c++ memory new-operator delete-operator


【解决方案1】:

为指针调用 delete 会将其指向的内存块标记为“释放内存”。它不会立即销毁内存中的项目。鉴于“删除”用于指向该内存块的指针,“项目”仅在进程返回后才会被销毁。如果不使用“删除”,它会永远留在那里,导致内存泄漏。

要解决您的问题,只需:

delete pTest;
pTest=NULL;

【讨论】:

    【解决方案2】:

    是时候了解什么是未定义行为了。 :)

    在 C++ 中,当你做一些非法/无意义/坏/等等的事情时。该标准经常说“它会导致未定义的行为”。这意味着从那时起,您的程序状态将完全不受保证,任何事情都可能发生。

    在您执行最后一个*(pTest) 时,您会得到未定义的行为。这是因为pTest 没有指向有效的对象,并且取消引用这样的指针是未定义的。所以你看到的是完全允许的:未定义的输出。

    您所做的只是说“我已完成此分配”。一旦你这么说,你不应该(事实上,不能)再检查或关心那个记忆。释放某些东西然后尝试使用它甚至在概念上都没有意义;你说你已经完成了!

    您的输出在某种程度上是可以预测的:很可能,您的操作系统只是说“好的,感谢您的记忆”,仅此而已。它没有理由真正“重置”内存,或做任何特别的事情。当没有人(包括您自己的程序)不使用它时,这确实是浪费时间。

    但是请记住,这个输出是完全未定义的。不要尝试使用不存在的对象。也许更好的测试是:

    #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.
    }
    

    虽然您似乎想看看内存本身会发生什么。请记住,摆脱记忆然后尝试使用它是没有意义的,所以答案是:谁知道呢。这取决于您的特定平台,C++ 并不关心。

    【讨论】:

    • 未定义行为的丑陋之处在于它大多数时候看起来不错。
    • 请注意,一些实现改变内存的内容,以帮助调试。例如,根据释放内存的方式,您可能会看到 0xDEADBEEF、0xFEEEFEEE 或 0xDDDDDDDD 等字节模式。
    • “从那一点向前”不仅仅是从未定义的行为向前,而是沿着所有最终以未定义行为结束的路径向后
    【解决方案3】:

    调用 delete 会将内存区域标记为空闲。它不需要重置它的旧值。

    建议你在调用delete后将指针设置为0:

    delete pTest;
    pTest = 0;
    

    【讨论】:

    • 我建议在调用delete 之后不要使用变量at all(...当然,除非它被重新分配给其他东西)。
    • 更好的解决方案是确保您的变量在删除后不久就超出范围,因此不能重复使用。
    【解决方案4】:

    答案是性能

    使用无效值(0xCCCCCCCC0xDEADDEAD 等)填充所有释放的内存以捕获使用指向已释放内存的陈旧指针的尝试是一个很好的调试帮助。

    但修改释放的内存会消耗 CPU 时间,因此出于性能原因,操作系统只会将释放的内存块添加到其“空闲”列表中,并保持内容不变。

    【讨论】:

      【解决方案5】:

      只是一个简单的例子来说明可能发生的事情,以及未定义的事情 一些人提到的行为。

      如果我们在打印前多加两行代码:

      delete pTest;
      
      int *foo = new int;
      *foo = 42;
      
      cout << *pTest << endl;
      

      pTest 的打印值很可能是 3,就像您的情况一样。 但是,打印的值也可能是 42。由于 pTest 指针被删除, 它的内存被释放了。因此, foo 指针可能会 指向内存中 pTest 之前指向的相同位置 已删除。

      【讨论】:

        【解决方案6】:

        取消引用指向已释放内存的指针是未定义的行为。

        很多时候它会起作用,因为new 提供的内存通常是分配器管理的更大块已分配内存的一部分。当你调用delete时,它会调用相关的析构函数并将内存标记为空闲,这通常意味着“准备重用”。因此,查看该内存,您将找到与调用 delete 之前相同的数据,或者如果在调用 new 之后重新分配了该内存块,则还有一些其他数据。

        请注意,new/delete 分配器可以作为操作系统虚拟内存函数的瘦包装器,因此当与页面相关的所有已分配块都被释放时,整个页面都会被释放,任何尝试访问它会导致地址冲突。

        TL,DR 版本:不要引用指向已释放内存的指针:它有时可能会起作用,有时会给你返回垃圾,有时会触发访问冲突。

        如果你犯了这种错误,立即注意到的一个好方法是在删除它们指向的内存后将指针设置为 NULL:如果你的代码试图取消引用 NULL 指针,几乎在任何系统上这都会使应用程序崩溃,因此不会忽视此类故障。

        【讨论】:

          【解决方案7】:

          销毁数据意味着什么?我想它可以把它归零,但为什么要麻烦呢?当我们从环境中得到它时,它被认为是脏的,那么为什么在我们返回它之前清洁它呢?我们不在乎里面有什么,因为我们放弃了阅读它的权利。至于为什么 delete 不会将指针本身归零:

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

          【讨论】:

            【解决方案8】:

            它可以引用任何映射内存。或者可能是未映射的内存,具体取决于您的程序执行了多长时间、内存分配的详细信息以及库是否稍后将内存返回给操作系统......

            如果delete 实际上清除了所有正在删除的内存,程序将花费更长的时间运行,因为它们会浪费大量时间清理内存,这些内存迟早会被覆盖。它可能对调试有好处,但在生产使用中,实际上清理内存内容的要求并不多。 (当然,加密密钥是一个很好的例外;在调用 deletefree 之前清理它们是个好主意。)

            【讨论】:

              【解决方案9】:

              delete 操作符调用对象的析构函数并释放之前分配给对象的内存。 不影响指向被删除对象的指针变量。

              因此,当取消引用指向已销毁对象的指针时,您会遇到麻烦。

              【讨论】:

              • 我认为问题实际上是为什么指向的内存仍然具有与对象被销毁之前相同的值,而不是为什么指针仍然指向这样的值。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-07-24
              • 2010-09-06
              • 2012-12-30
              • 2014-07-03
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多