【问题标题】:Why is there a special new and delete for arrays?为什么数组有特殊的new和delete?
【发布时间】:2010-10-14 03:24:45
【问题描述】:

使用delete 而不是delete[] 有什么问题?

在分配和释放数组的背后是否发生了一些特别的事情?

为什么会不同于malloc和免费?

【问题讨论】:

    标签: c++ arrays memory-management new-operator


    【解决方案1】:

    使用new[] 创建的对象必须使用delete[]。使用 delete 在数组上是未定义的。

    使用 malloc 和 free 时,情况会更简单。只有 1 个函数可以释放您分配的数据,也没有调用析构函数的概念。因为delete[] 和delete 看起来很相似,所以才出现混乱。实际上它们是两个完全不同的功能。

    使用 delete 不会调用正确的函数来删除内存。它应该调用delete[](void*),但它调用delete(void*)。出于这个原因,您不能依赖使用delete 来分配使用new[] 的内存

    See this C++ FAQ

    [16.13] 我可以在什么时候删除[] 删除某些内置类型的数组 (char、int 等)?

    不!

    有时程序员认为 []delete[] p 中只存在所以 编译器将调用适当的 中所有元素的析构函数 大批。正因为如此,他们 假设一些内置的数组 类型如charint可以 deleted 没有 []。例如,他们 假设以下是有效代码:

    void userCode(int n)  {
        char* p = new char[n];
        ...
        delete p; // ← ERROR! Should be delete[] p !
    }
    

    但是上面的代码是错误的,它 可能会在运行时造成灾难。在 特别是调用的代码 delete poperator delete(void*), 但是要求的代码 delete[] poperator delete[](void*)。默认行为 因为后者是调用前者, 但允许用户更换 后者具有不同的行为(在 在这种情况下,他们通常也会 替换相应的新代码 运营商new[](size_t))。如果他们 替换了delete[] 代码,所以它 与删除不兼容 代码,你叫错了 (即,如果您说 delete p 而不是 比delete[] p),你最终可能会 在运行时发生灾难。

    为什么delete[]首先存在?

    无论你做 x 还是 y:

     char * x = new char[100]; 
     char * y = new char;
    

    两者都存储在char * 类型变量中。

    我认为deletedelete[] 的决定的原因与一长串有利于C++ 效率的决定有关。这样就没有强制的价格来查找正常删除操作需要删除多少。

    为了对称,拥有两个newnew[] 似乎只有deletedelete[] 才合乎逻辑。

    【讨论】:

    • 也许您可以在答案中添加有关覆盖各种新/删除的信息/链接以回答后面的问题。
    • @Brian 出于好奇:为什么需要区分两者?如,为什么 delete[] 首先存在?
    【解决方案2】:

    不同的是delete只会删除整个内存范围,而只会为1个对象调用析构函数。 delete[] 将删除内存并为每个对象调用析构函数。如果您不对数组使用delete[],那么将资源泄漏引入应用程序只是时间问题。

    编辑更新

    根据标准,将分配有new[] 的对象传递给delete 是未定义的。 可能的行为是它会像我描述的那样运行。

    【讨论】:

    • 那么为什么删除[] char 数组是个好习惯呢?只是为了保持习惯?
    • 嗯,我很确定对 new[] 的结果调用 delete (反之亦然)是未定义的行为。因此,它可能会导致比仅仅泄漏内存更糟糕的情况。
    • @derobert,我不能 100% 确定它是否已定义,但它肯定会造成不良行为。
    • 但是,在某些情况下,您必须在 new 的结果上调用 delete[]。
    • 所有新建数组的情况 :) 比如 typedef int foo[5]; int p = 新的 foo; / 删除[]: !! */ 删除[] p;
    【解决方案3】:

    Stroustrup 在第 10.3 节到第 10.5.1 节的“C++ 的设计和演进”中谈到了将 new/new[]delete/delete[]` 运算符分开的原因:

    • 10.3 数组分配 - 讨论了他们想要一种方法,允许使用与分配单个对象不同的方案来分配对象数组(即,从单独的存储区分配数组)。添加newdelete 的数组版本是一个解决方案;
    • 10.5.1 解除分配数组 - 讨论仅使用单个 delete 运算符解除分配数组的问题是,需要更多信息而不仅仅是指针,以确定指针是否指向数组的第一个元素,或者它只指向一个对象。 delete[] 运算符用于处理数组,而不是“使分配和释放单个对象的常见情况复杂化”。这符合“不为不使用的东西付费”的一般 C++ 设计理念。

    这个决定是否是一个错误值得商榷 - 无论哪种方式都有很好的论据,但我们拥有我们所拥有的。

    【讨论】:

      【解决方案4】:

      此要求的原因是历史性的,因为 new typenew type [size] 返回需要以不同方式清理的不同内容。

      考虑这段代码

      Foo* oneEntry = new Foo;
      Foo* tenEntries = new Foo[10];
      

      它们都返回一个Foo* 指针,不同之处在于第二次调用将导致Foo 构造函数被调用10 倍,并且内存大约是10 倍。

      所以现在你想释放你的对象。

      对于单个对象,您可以调用 delete - 例如delete oneEntry。这会调用对象的析构函数并释放内存。

      但问题出在这里 - oneEntry 和 tenEntries 都只是 Foo 指针。编译器不知道它们指向一个、十个还是一千个元素。

      当您使用delete [] 的特殊语法时。这告诉编译器“这是一个对象数组,计算出计数然后将它们全部销毁”。

      真正发生的是,对于new type [size],编译器秘密地将“大小”存储在其他地方。当您调用 delete[] 时,它知道这个秘密值存在,因此它可以找出该内存块中有多少对象并销毁它们。

      然后您可能会问的问题是“为什么编译器不总是存储大小?”

      这是一个很好的问题,它可以追溯到 C++ 的早期。人们希望对于内置类型(char、int、float 等),以下内容对 C++ 有效;

      int* ptr = new int;
      free(ptr);
      
      int* ptr = (int*)malloc(sizeof(int) * someSize);
      delete ptr;
      

      这背后的原因是人们期望人们会提供返回动态分配内存的库,而这些库的用户将无法知道是否使用 free/delete。

      这种对兼容性的渴望意味着数组的大小不能作为数组本身的一部分存储,而必须保存在其他地方。由于这种开销(请记住,这可以追溯到 80 年代初),因此决定只为数组而不是单元素进行此簿记。因此数组需要一个特殊的删除语法来查找这个值。

      malloc/free之所以没有这个问题,是因为他们只处理内存块,不用担心调用构造函数/析构函数。

      【讨论】:

        【解决方案5】:

        至于标题中的“为什么”:C++ 的设计目标之一是不存在任何隐藏成本。 C++ 也是在每个内存字节仍然比今天重要得多的时候开发的。语言设计者也喜欢正交性:如果你用new[](而不是new)分配内存,你应该用delete[]释放它。

        我认为new[] 无法在delete 的内存块的标题中粘贴“我是一个数组”标志没有任何技术原因(不再有delete[]) 稍后再看。

        【讨论】:

          【解决方案6】:

          newdeletemallocfree 的不同之处在于mallocfree 仅分配和释放内存;他们不叫ctors或dtors。

          【讨论】:

            【解决方案7】:

            当您使用new[] 分配一个数组时,您实际上是在告诉C++ 数组的大小。当您使用malloc 时,您是在告诉它分配了多少内存。在前一种情况下,根据数组的大小进行释放是没有意义的。在这种情况下,确实如此。但是由于数组的指针与单个对象的指针没有区别,因此需要一个单独的函数。

            【讨论】:

              猜你喜欢
              • 2014-12-12
              • 1970-01-01
              • 2012-01-13
              • 1970-01-01
              • 2021-09-13
              • 1970-01-01
              • 2011-11-11
              • 1970-01-01
              • 2021-03-28
              相关资源
              最近更新 更多