68

我偶然发现了一个涉及不同大小的数组声明的测验。我首先想到的是我需要在new命令中使用动态分配,如下所示:

while(T--) {
   int N;
   cin >> N;
   int *array = new int[N];
   // Do something with 'array'
   delete[] array;
}

但是,我看到其中一种解决方案允许以下情况:

while(T--) {
    int N;
    cin >> N;
    int array[N];
    // Do something with 'array'
}

经过一番研究,我读到 g++ 允许这样做,但它让我一直在思考,在哪些情况下需要使用动态分配?还是编译器将其翻译为动态分配?

包括删除功能。但是请注意,这里的问题与内存泄漏无关。

4

7 回答 7

117

您展示的两个片段都不是惯用的现代 C++ 代码。

newand delete(and new[]and delete[]) 在 C++ 中不会被弃用,也永远不会被弃用。它们仍然实例化动态分配对象的方法。但是,由于您必须始终将 anew与 a delete(和 anew[]与 a delete[])匹配,因此最好将它们保存在(库)类中,以确保为您做到这一点。请参阅为什么 C++ 程序员应尽量减少“新”的使用?.

您的第一个片段使用“裸” new[],然后从不delete[]创建数组。那是个问题。std::vector在这里你需要的一切都很好。它将new在幕后使用某种形式(我不会深入探讨实现细节),但您需要注意的是,它是一个动态数组,但更好更安全。

您的第二个片段使用“可变长度数组”(VLA),这是一些编译器还允许在 C++ 中作为扩展的 C 功能。与 不同new的是,VLA 本质上是在堆栈上分配的(资源非常有限)。但更重要的是,它们不是标准的 C++ 特性,应该避免使用,因为它们不可移植。它们当然不会取代动态(即堆)分配。

于 2020-01-20T10:14:18.663 回答
25

好吧,对于初学者来说,new/delete并没有被弃用。

不过,在您的具体情况下,它们并不是唯一的解决方案。您选择的内容取决于您的“使用数组做某事”评论下隐藏的内容。

您的第二个示例使用了一个非标准的 VLA 扩展,它试图将数组放入堆栈。这有一定的限制——即有限的大小和在数组超出范围后无法使用此内存。您不能将其移出,它会在堆栈展开后“消失”。

因此,如果您的唯一目标是进行本地计算然后丢弃数据,它实际上可能工作正常。但是,更强大的方法是动态分配内存,最好使用std::vector. 这样,您就可以根据运行时值(这是我们一直以来的目标)为所需数量的元素创建空间,但它也会很好地清理自己,并且您可以将其移出如果您想保留内存以供以后使用,请在此范围内使用。

回到开头,可能vector 使用new更深的几层,但你不应该担心这一点,因为它呈现的界面要优越得多。从这个意义上说,使用newanddelete可以被认为是不鼓励的。

于 2020-01-20T10:13:43.763 回答
16

您的第二个示例使用可变长度数组(VLA),它实际上是C99不是C++!)功能,但仍受g++支持。

另请参阅此答案

请注意,可变长度数组与new/不同,delete并且不会以任何方式“弃用”它们。

还要注意 VLA不是ISO C++。

于 2020-01-20T10:12:15.903 回答
14

现代 C++ 提供了更简单的方法来处理动态分配。一旦引用的数据结构超出范围,智能指针可以处理异常(可能发生在任何地方,如果允许的话)和提前返回后的清理,因此使用这些可能是有意义的:

  int size=100;

  // This construct requires the matching delete statement.
  auto buffer_old = new int[size];

  // These versions do not require `delete`:
  std::unique_ptr<int[]> buffer_new (new int[size]);
  std::shared_ptr<int[]> buffer_new (new int[size]); 
  std::vector<int> buffer_new (size);  int* raw_access = buffer_new.data();

从 C++ 14 你也可以写

auto buffer_new = std::make_unique<int[]>(size);

如果分配失败,这看起来更好,并且可以防止内存泄漏。从 C++ 20 开始,您应该能够做到

auto a = std::make_shared<int[]>(size);

这对我来说仍然没有在使用 gcc 7.4.0 编写时编译。在这两个示例中,我们还使用auto了左侧的类型声明来代替。在所有情况下,像往常一样使用数组:

buffer_old[0] = buffer_new[0] = 17;

内存泄漏new和双倍崩溃delete是 C++ 多年来一直受到抨击的问题,它是切换到其他语言的争论的“中心点”。也许最好避免。

于 2020-01-21T10:12:14.563 回答
3

newdelete不会被弃用。

new 运算符创建的对象可以通过引用传递。可以使用 delete 删除对象。

new 和 delete 是该语言的基础方面。可以使用 new 和 delete 管理对象的持久性。这些绝对不会被弃用。

语句 - int array[N]是定义数组的一种方式。该数组可以在封闭代码块的范围内使用。它不能像对象传递给另一个函数那样传递。

于 2020-01-26T12:19:29.120 回答
2

第一个示例delete[]最后需要 a ,否则会出现内存泄漏。

第二个例子使用了 C++ 不支持的可变数组长度;它只允许数组长度的常量表达式

在这种情况下,用作解决方案很有用std::vector<>;它将您可以对数组执行的所有操作包装到模板类中。

于 2020-01-20T10:15:12.293 回答
-4

语法看起来像 C++,但成语类似于普通的旧 Algol60。有这样的代码块很常见:

read n;
begin
    integer array x[1:n];
    ... 
end;

这个例子可以写成:

while(T--) {
    int N;
    cin >> N;
    {
        int array[N];
        // Do something with 'array'
    }
}

我有时会错过当前语言中的这一点;)

于 2020-01-23T18:01:56.450 回答