【问题标题】:When do you need to delete C-style arrays?什么时候需要删除 C 风格的数组?
【发布时间】:2019-07-08 08:42:30
【问题描述】:

我正在阅读an excellent answer 另一个问题,其中给出了这些具体示例:

char[] array = {'a', 'b', 'c', '\0'};
Thing* t = new Thing[size];
t[someindex].dosomething();

我评论说,作为一种好的做法,您应该在第二个示例中添加delete[],因为这是人们忘记导致内存泄漏的位。

但我不记得你是否需要首先delete[]。我不认为你这样做,因为没有 new 所以它在堆栈上,但我不能 100% 确定这是正确的。

【问题讨论】:

  • newdelete 成对出现。没有delete 没有new,反之亦然。
  • @ThomasSablik 通常是的,但是当例如用new 制作unique_ptr 时(你实际上不应该这样做),你仍然不应该在指针上使用delete .
  • @ruohola 在内部它仍然使用delete 运算符,所以托马斯声称仍然有效。
  • @Neijwiert 是的,只是在某些情况下程序员只使用其中一个就可以了。
  • 由于 C++11 直接使用 new delete 已过时且不推荐。尽可能使用 RAII。如果给出的示例也应使用 C++03 std::vector

标签: c++ arrays


【解决方案1】:

我不认为你这样做,因为没有新的所以它在堆栈上,但我不能 100% 确定这是正确的。

array 在函数内部定义并因此在堆栈中时,此推理是正确的。您必须永远不要在没有使用new 的情况下在堆栈上创建的任何对象上使用delete。在堆栈分配的对象上使用delete 将导致未定义的行为。

如果定义 char[] array = {'a', 'b', 'c', '\0'}; 出现在文件范围内,但是,它将具有静态存储持续时间并且不会在 堆栈上,但它仍然不会已动态分配,但仍不能为delete[]d。

所以,无论哪种方式,都不要delete[] 数组....

【讨论】:

    【解决方案2】:
    1. 当您使用newdelete[] 当您使用new[] 时,您始终且仅使用delete。切勿混合它们。切勿与 (2) 混合使用。
    2. 当您进行了经典的 C 分配(如 malloccalloc)时,您始终且仅使用 free。切勿与 (1) 混合。但不要释放使用alloca 分配的内存;)

    请注意,在某些情况下,分配或解除分配可以隐藏在函数或其他内容中。例如,有一个非标准的 C 函数可以复制这样的字符串:char *str = strdup("Hello");。在这种情况下,您应该使用free(str),即使您没有手动调用malloc

    因此,如果您在 T t[10]T[] t={a,b,c} 表单上进行声明,则不会取消分配。

    int main()
    {
        int x;
        std::cin << x;
    
        T *a = new T;
        delete a;
        T *b = new T[x];
        delete[] b;
        T *c = (T*)malloc(x*sizeof(*c)); // Casting is needed in C++ but not in C
        free(c);
    
        // No free or delete for these
        T d;
        T e[10];
        // Assuming a, b and c are instances of T or that you have overloaded the
        // operator so that you can write T t=a; 
        // In either case, you should not free nor delete this
        T f[] = {a, b, c};
    }
    

    在 C 中,您可以使用 VLA:s,例如 int arr[x],其中 x 在编译时是未知的。这在 C++ 中是不可能的。你也不能在这些数组上使用 free。

    我不认为你这样做,因为没有新的所以它在堆栈上,但我不能 100% 确定这是正确的。

    这是不正确的。该标准没有规定对象应该在堆栈上还是堆上。这取决于编译器,所以实际上发生的事情是编译器通常如何解决任务的问题。

    此外,静态分配的内存最终在堆栈上而动态分配的内存最终在堆上的“事实”在大多数情况下都是正确的。当然,这通常是会发生的,但在极少数情况下不会发生这种情况。而且这些事件并没有那么罕见或晦涩难懂,以至于可以忽略不计。全局变量通常不会在堆栈中结束。

    因此,是否应该释放/删除内存不是它是否位于堆栈或堆中的问题。这是关于它是如何分配的。

    在现代 C++ 中,您很少使用 newnew[]deletedelete[]。相反,您使用智能指针和容器。看看这个链接: What is a smart pointer and when should I use one?https://en.cppreference.com/w/cpp/container/vector 如果您正在处理旧的遗留 C++,将其重构为现代内存管理可能是一个好主意。如果您将 new-delete 对更改为智能指针,那么您已经保存了一行。如果您更改了new 而没有相应的delete,那么您已经解决了一个错误。

    TL;DR

    什么时候需要删除 C 风格的数组?

    对于T a[n] 的情况(有或没有初始化),你永远不应该这样做。对于T *a = new T[n] 的情况,您应该始终这样做。每当您使用new 时,您都应该在之后使用delete,而不是其他方式。 (这可能有一些罕见的例外。请参阅帖子的其余部分。)

    另外,AFIK,“C 样式数组”没有既定定义。就个人而言,我根本不会考虑 T *a = new T[n] 一个数组。我也不会考虑 T *a = malloc(n*sizeof(*a)) 一个数组。原因很简单。指针和数组不是一回事,这适用于 C 和 C++。这个问题是关于 C 的,但也适用于 C++:Is an array name a pointer?

    【讨论】:

      【解决方案3】:

      我评论说,作为一种好的做法,您应该将 delete[] 添加到 第二个例子,因为这是人们忘记导致记忆的一点 泄漏。

      您还应该添加delete[] 以调用析构函数。除了释放一些内部分配的内存之外,析构函数可能还有其他副作用。

      在语义上,newdelete(以及 new[]delete[])成对出现。 new 启动对象生命周期,delete 结束由 new 启动的对象生命周期。但是,通常建议将删除隐藏在智能指针中。

      这个想法是将对象生命周期绑定到“对象所有权”的概念。静态对象归整个程序所有。自动对象由定义它们的代码块拥有。动态对象在 C++ 语言本身中没有明确定义的所有权,尽管 C++ 语言提供了工具来实现代码编写者的对象所有权概念。

      智能指针就是这些工具之一。 std::unique_ptr 用于对象在任何时候只有一个所有者,但您可能希望在代码块之间传递所有权。 std::shared_ptr 在可以有多个所有者时使用,并且对象生命周期应在最后一个所有者停止对该对象感兴趣时结束。

      【讨论】:

      • 虽然我知道在 C++ 中到目前为止,far 存在比 C 样式数组更好的选项,但它们仍然存在于遗留代码和 retain niche uses in C++ 中,因此本文的范围问题严格限于 c 数组,而不是更现代的替代品。托管指针似乎更超出范围。
      • 实际上,你很幸运,你有任何堆(我的一个项目中没有它,只应该在另一个项目中使用区域分配器)。尽管如此,(正确地)将智能指针与 C 样式数组一起使用并没有错。
      猜你喜欢
      • 2018-11-11
      • 1970-01-01
      • 2012-09-16
      • 2012-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-10
      • 2010-12-29
      相关资源
      最近更新 更多