【问题标题】:Is delete p where p is a pointer to array always a memory leak?删除 p 其中 p 是指向数组的指针总是内存泄漏吗?
【发布时间】:2010-03-09 09:01:51
【问题描述】:

在一次软件会议上进行讨论后,我着手确定删除具有纯 delete 的动态分配的原始数组是否会导致内存泄漏。

我编写了这个小程序,并在 windows XP 上运行 Visual Studio 2008 编译它:

#include "stdafx.h"
#include "Windows.h"

const unsigned long BLOCK_SIZE = 1024*100000;
int _tmain()
{
    for (unsigned int i =0; i < 1024*1000; i++)
    {
        int* p = new  int[1024*100000];
        for (int j =0;j<BLOCK_SIZE;j++) p[j]= j % 2;
        Sleep(1000);
        delete p;
    }
}

然后我使用任务管理器监控我的应用程序的内存消耗,令人惊讶的是内存被正确分配和释放,分配的内存并没有像预期的那样稳定增加

我已经修改了我的测试程序来分配一个非原始类型数组:

#include "stdafx.h"
#include "Windows.h"


struct aStruct
{
    aStruct() : i(1), j(0) {}

    int i;
    char j;
} NonePrimitive;

const unsigned long BLOCK_SIZE = 1024*100000;
int _tmain()
{
    for (unsigned int i =0; i < 1024*100000; i++)
    {
        aStruct* p = new  aStruct[1024*100000];
        Sleep(1000);
        delete p;

    }
}

运行 10 分钟后内存没有明显增加

我编译了警告级别为 4 的项目,但没有收到任何警告。

Visual Studio 运行时是否有可能跟踪分配的对象类型,因此在该环境中 deletedelete[] 之间没有区别?

【问题讨论】:

  • @Philip Potter:不是那个问题——那个问题是专门关于导致内存泄漏的。在这种情况下,内存泄漏并不典型。
  • 尝试使用 shared_ptr&lt;int&gt; 数组,每个数组指向不同的分配 int,您将看到您的实现是否使 deletedelete[] 等效。 C++ 对人们知道是错误的东西的迷恋总是在标准中明确指出是错误的,但似乎有时它们可​​能只是碰巧起作用;-)
  • @Steve Jessop:unfurtinalty 它让人们在使用参数时看起来很愚蠢:“它在标准中未定义,因此不应该使用”。 MS 在执行标准时放宽规则的习惯导致开发人员在编写无投诉代码时会产生错误的安全感/
  • 更不用说地球上没有开发人员可以证明这种说法是正确的,“啊,是的,我完全理解 deletedelete[] 之间的区别,但是对于仅 MSVC 中的 POD 类型的数组代码,我故意巧妙地使用delete,作为节省输入两个额外字符的优化”。不,你没有,你只是忘记了。承认错误,纠正错误,不要再浪费时间争论了 ;-)

标签: c++ memory-management


【解决方案1】:

删除 p,其中 p 是一个数组,称为未定义行为。

具体来说,当你分配一个原始数据类型(ints)的数组时,编译器并没有太多的工作要做,所以它把它变成了一个简单的malloc(),所以delete p 可能会起作用。

delete p 会失败,通常在以下情况下:

  • p 是一个复杂的数据类型 - 删除 p;不知道调用单独的析构函数。
  • “用户”重载运算符 new[] 和 delete[] 以使用与常规堆不同的堆。
  • 调试运行时重载运算符 new[] 和 delete[] 为数组添加额外的跟踪信息。
  • 编译器决定它需要与对象一起存储额外的 RTTI 信息,这会删除 p;不明白,但删​​除 []p;会的。

【讨论】:

    【解决方案2】:

    不,这是未定义的行为。不要这样做 - 使用delete[]

    在 VC++ 7 到 9 中,它恰好可以工作 when the type in question has trivial destructor,但它可能会停止在较新的版本上工作——通常是具有未定义行为的东西。无论如何不要这样做。

    【讨论】:

    • 很好地解释了为什么它可能会在 @Eli 的案例中起作用!
    【解决方案3】:

    这叫做未定义的行为;它可能有效,但你不知道为什么,所以你不应该坚持下去。

    我认为 Visual Studio 不会跟踪您如何分配对象(作为数组或普通对象),并且会神奇地将 [] 添加到您的删除中。它可能会将delete p; 编译为与分配p = new int 相同的代码,并且正如我所说,由于某种原因它可以工作。 但你不知道为什么。

    【讨论】:

      【解决方案4】:

      一个答案是,是的,它会导致内存泄漏,因为它不会为数组中的每个项目调用析构函数。这意味着数组中的项目所拥有的任何额外内存都会泄漏。

      更符合标准的答案是它是未定义的行为。例如,编译器有权为数组使用与非数组项不同的内存池。执行新的一种方式但删除另一种方式可能会导致堆损坏。

      您的编译器可能会保证标准没有,但第一个问题仍然存在。对于不拥有额外内存(或文件句柄等资源)的 POD 项目,您可能没问题。

      即使它对您的编译器和数据项是安全的,也不要这样做 - 这也会误导任何试图阅读您的代码的人。

      【讨论】:

        【解决方案5】:

        不,你应该在处理数组时使用delete[]

        【讨论】:

          【解决方案6】:

          仅使用delete 不会调用数组中对象的析构函数。虽然它会可能按预期工作,但它是未定义的,因为它们的工作方式存在一些差异。所以你不应该使用它,even for built in types

          【讨论】:

          • FAQ 有很强的论据,谢谢指出
          【解决方案7】:

          似乎没有泄漏内存的原因是因为 delete 通常是基于 free 的,它已经知道它需要释放多少内存。但是,c++ 部分不太可能被正确清理。我敢打赌,只有第一个对象的析构函数被调用。

          【讨论】:

            【解决方案8】:

            使用带有 [] 的 delete 告诉编译器在数组的每个项目上调用析构函数。 如果在使用动态内存分配的对象数组上使用,不使用 delete [] 可能会导致内存泄漏,如下所示:

            class AClass
            {
            public:
                AClass()
                {
                    aString = new char[100];
                }
                ~AClass()
                {
                    delete [] aString;
                }
            private:
                const char *aString;
            };
            
            int main()
            {
                AClass * p = new  AClass[1000];
                delete p; // wrong
                return 0;
            }
            

            【讨论】:

              猜你喜欢
              • 2019-03-31
              • 1970-01-01
              • 2021-06-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-10-19
              • 2012-09-08
              • 2014-02-19
              相关资源
              最近更新 更多