C ++中是否不赞成使用“ new”和“ delete”?


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 ++允许这样做,但是这让我一直思考,那么在什么情况下有必要使用动态分配?还是编译器将此转换为动态分配?

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


54
第二个示例使用一个可变长度数组,该数组从未成为C ++的一部分。在这种情况下,请std::vector改用(std::vector<int> array(N);)。
一些程序员哥们

7
您问题的直接答案应该是:不,它不会过时。即使现代版本的C ++提供了许多简化内存所有权管理(智能指针)的功能,但仍然普遍通过new OBJ直接调用对象来分配对象。
pptaszni

8
对于其他对为什么人们在谈论内存泄漏感到困惑的人,对该问题进行了编辑,以更正对该问题并不重要的错误
Mike Caron

4
@Mannoj倾向于使用术语“动态”和“自动”进行堆放。很少见,但是有可能在没有堆和栈的情况下实现C ++。
user4581301

1
在C ++中,什么都不会被弃用,也不会。这就是C ++含义的一部分。
JoelFan

Answers:


114

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

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

您的第一个代码段使用“裸” new[],然后从不delete[]创建数组。那是个问题。std::vector在这里您需要的一切都很好。它将使用某种形式的new后台程序(我不会深入介绍实现细节),但是您需要关心的是,它是一个动态数组,但是更好,更安全。

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


3
我想补充一点,尽管VLA尚未正式纳入标准,但所有主要编译器都支持它们,因此,是否避免使用VLA的决定更多是样式/偏好问题,而不是实际的可移植性问题。
Stack Tracer

4
还要记住,您不能返回数组或将其存储在其他位置,因此VLA永远不会超过该函数的执行时间
Ruslan

16
就我所知,@ StackTracer MSVC不支持VLA。MSVC绝对是“主要编译器”。
Max Langhof

2
“您必须始终将一个新的删除与之匹配” -如果您使用Qt,则不可以,因为它的基类都具有垃圾收集器,因此new大多数情况下,您只是使用它而忽略它。对于GUI元素,当父窗口小部件关闭时,子窗口超出范围并被自动垃圾回收。
vsz

6
@vsz即使在Qt中,每个new仍然有一个匹配项delete;只是deletes是由父窗口小部件完成的,而不是与news 在同一代码块中。
jjramsey

22

好吧,对于初学者,new/ delete不会被弃用。

但是,在您的特定情况下,它们并不是唯一的解决方案。您选择什么取决于隐藏在“用数组执行操作”注释下的内容。

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

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

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


1
注意“ ...更深一些层”。如果要实现自己的容器,则仍应避免使用newand delete,而应使用诸如的智能指针std::unique_pointer
最多

1
实际称为std::unique_ptr
user253751

2
@Max:std::unique_ptr的默认析构函数调用deletedelete[],这意味着必须由new或分配已拥有的对象,或者自C ++ 14起new[]就隐藏了这些调用std::make_unique
洛朗LA RIZZA

15

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

另请参阅此答案

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

另请注意,VLA 不是 ISO C ++。


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();

从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 ++多年以来遭受挫败的原因,这是切换到其他语言的争论的“中心点”。最好避免。


您应该避免使用unique/shared_ptr构造函数make_unique/shared,而不是(auto不必使用)两次编写构造的类型,而且如果构造中途失败(如果使用的类型可能会失败),也不必冒泄漏内存或资源的风险。
西蒙·布坎

2
make_unique可用于C ++ 14中的数组,而make_shared仅可用于C ++ 20中。这仍然很少是默认设置,因此建议std :: make_shared <int []>(size)提前找我一下。
Audrius Meskauskas

很公平!make_shared<int[]>当您几乎总是想要时vector<int>,我真的用不了多少,但很高兴知道。
西蒙·布坎

过多的装饰,但是IIRC的unique_ptr构造函数没有抛出异常,因此对于T没有抛出异常的构造函数,因此没有泄漏的风险,unique_ptr(new int[size])并且shared_ptr具有以下内容:“如果抛出异常,则在T不是数组类型时调用delete p,delete [ ] p否则。”,则您具有相同的效果-风险为unique/shared_ptr(new MyPossiblyAllocatingType[size])
西蒙·布坎

3

不推荐使用newdelete

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

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

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


2

第一个示例最后需要一个a delete[],否则您将发生内存泄漏。

第二个示例使用C ++不支持的可变数组长度。它仅允许常数表达式为数组长度

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


3
“直到C ++ 11”是什么意思?我敢肯定,VLA从未成为标准的一部分。
churill

参阅第184页第8.3.4段中的c ++ 14 [c ++ 14 standard](isocpp.org/files/papers/N3690.pdf)标准
Zig razor

4
那不是。标准的,但只有一个草案,关于部分“运行时的阵列约束”,没有使之成为标准的,据我可以告诉cppreference没有提到沃拉斯那里。
churill

1
@zigrazor cppreference.com 列出了每个标准发布之前/之后最接近的草案的链接列表。出版的标准不是免费提供的,但是这些草案应该非常接近。从文档编号中可以看到,链接的草案是C ++ 14的较旧的工作草案。
胡桃

2
@learning_dude 标准支持。答案是(现在)正确(尽管简短)。它仅对您有效,因为GCC允许它作为非标准扩展。
胡桃

-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'
    }
}

我有时会错过当前的语言;)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.