'new' 和 'delete' 在 C++ 中被弃用了吗?

lea*_*ude 68 c++ arrays dynamic-memory-allocation static-memory-allocation

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

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

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

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

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

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

Max*_*hof 117

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

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

您的第一个代码段使用“裸” new[],然后从不delete[]使用创建的数组。这是个问题。std::vector在这里你需要的一切都很好。它将使用某种形式new的幕后(我不会深入研究实现细节),但对于您必须关心的所有内容,它是一个动态数组,但更好更安全。

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

  • @StackTracer 据我所知,MSVC 不支持 VLA。MSVC 绝对是一个“主要编译器”。 (16认同)
  • @vsz 即使在 Qt 中,每个“new”仍然有一个匹配的“delete”;只是“删除”是由父小部件完成的,而不是在与“新”相同的代码块中完成的。 (6认同)
  • 另请记住,您无法返回数组或将其存储在其他地方,因此 VLA 的寿命不会超过函数的执行时间 (4认同)
  • 我想补充一点,虽然 VLA 并未正式纳入标准,但所有主要编译器都支持它们,因此是否避免使用它们的决定更多的是风格/偏好问题,而不是对可移植性的现实考虑。 (3认同)
  • *“你必须始终将 new 与删除相匹配”* - 如果你使用 `Qt`,则不会,因为它的基类都有垃圾收集器,所以大多数时候你只需使用 `new` 并忘记它。对于 GUI 元素,当父窗口小部件关闭时,子窗口小部件将超出范围并自动进行垃圾收集。 (2认同)
  • 如果您包含一个惯用的现代 C++ 代码示例,这个答案会更好 (2认同)

Bar*_*icz 24

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

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

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

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

盘旋回到起点,vector 可能使用new几层深,但你不应该与关注,因为它提出的界面优越得多。从这个意义上说,使用newdelete可以被认为是不鼓励的。

  • @Max : `std::unique_ptr` 的默认析构函数调用 `delete` 或 `delete[]`,这意味着拥有的对象无论如何都必须由 `new` 或 `new[]` 分配,这些调用已被自 C++14 起隐藏在 `std::make_unique` 中。 (3认同)
  • 请注意“......更深几层”。如果您要实现自己的容器,您仍然应该避免使用“new”和“delete”,而是使用“std::unique_pointer”等智能指针。 (2认同)

and*_*eee 16

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

另请参阅此答案

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

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


h22*_*h22 13

现代 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();
Run Code Online (Sandbox Code Playgroud)

从 C++ 14 你也可以写

auto buffer_new = std::make_unique<int[]>(size);
Run Code Online (Sandbox Code Playgroud)

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

auto a = std::make_shared<int[]>(size);
Run Code Online (Sandbox Code Playgroud)

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

buffer_old[0] = buffer_new[0] = 17;
Run Code Online (Sandbox Code Playgroud)

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

  • make_unique 可用于 C++14 中的数组,而 make_shared 仅可用于 C++20 中。这仍然很少是默认设置,因此提议 std::make_shared&lt;int []&gt;(size) 有点提前找我。 (2认同)