delete和delete[]之间有什么区别?

210
在C++中,deletedelete[]运算符有什么区别?

你可能会发现这个问题很相关:https://dev59.com/UXI-5IYBdhLWcg3wVWpi - sharptooth
7
使用智能指针和尽可能使用vector<>代替数组是因为delete和delete[]容易出问题的原因之一。 - David Thornley
1
https://dev59.com/JHI_5IYBdhLWcg3wDOdC - sbi
3
如果您正在使用智能指针,仍然需要知道它们的区别,因为您仍然需要知道不要编写例如 std::unique_ptr<int>(new int[3]) 的代码,因为它将在数组上调用常规的 delete 操作,这是未定义的行为。相反,您需要使用 std::unique_ptr<int[]> - Arthur Tacca
1
@DavidThornley 当你预先知道数组的大小时,最好使用 std::array,因为它在概念上更符合该用例,并且很可能更快。 - user904963
7个回答

240

delete 操作符释放内存并调用使用 new 创建的单个对象的析构函数。

delete [] 操作符释放内存并调用使用 new [] 创建的一组对象的析构函数。

在指向由 new [] 创建的指针上使用 delete 或在指向由 new 创建的指针上使用 delete [] 都会产生未定义的行为。


4
我想知道在使用delete释放一个包含基本类型(如int或char,没有构造函数/析构函数)的new[]数组时,是否一定会导致未定义行为。看起来,当使用基本类型时,数组大小并没有存储在任何地方。 - thomiel
34
如果标准没有定义进行该操作时的行为,那么根据定义它是“未定义的行为”,即使您的编译器确定性地执行您想要执行的操作。另一个编译器可能会执行完全不同的操作。 - Rob K
1
当我有一个C字符串数组,比如“char ** strArray”时,我犯了这个错误。如果你像我一样有一个数组,你需要遍历数组并删除/释放每个元素,然后删除/释放strArray本身。在我拥有的数组上使用“delete []”是不起作用的,因为(正如上面的评论和答案所指出的那样),它调用析构函数,而不是实际上释放每个插槽。 - Katianie

108
delete[] 运算符用于删除数组。 delete 运算符用于删除非数组对象。 它们分别调用 operator delete[]operator delete 函数来删除数组或非数组对象占用的内存,最终调用数组元素或非数组对象的析构函数。以下是它们之间的关系:
typedef int array_type[1];

// create and destroy a int[1]
array_type *a = new array_type;
delete [] a;

// create and destroy an int
int *b = new int;
delete b;

// create and destroy an int[1]
int *c = new int[1];
delete[] c;

// create and destroy an int[1][2]
int (*d)[2] = new int[1][2];
delete [] d;

对于创建数组的new(即new type[]或应用于数组类型构造的new),标准会在数组元素类型类或全局范围内查找operator new[],并传递所请求的内存量。如果需要(例如为了存储元素数量,以便在删除时知道要调用多少个析构函数),它可能会请求超过N * sizeof(ElementType)的内存量。如果该类声明了一个额外接受另一个size_t的内存量的operator new[],那么第二个参数将接收分配的元素数-它可以将其用于任何目的(调试等...)。
对于创建非数组对象的new,它将在元素类或全局范围内查找operator new。它传递所请求的内存量(始终精确为sizeof(T))。
对于delete[],它查找数组元素类类型并调用它们的析构函数。使用的operator delete[]函数是元素类型类中的函数,如果没有,则在全局范围内查找。
对于delete,如果传递的指针是实际对象类型的基类,则基类必须有虚析构函数(否则,行为是未定义的)。如果它不是基类,则调用该类的析构函数,并使用该类中的operator delete或全局operator delete。如果传递了一个基类,则调用实际对象类型的析构函数,并使用在该类中找到的operator delete,如果没有,则调用全局operator delete。如果类中的operator delete具有第二个类型为size_t的参数,则它将接收要释放的元素数。

如果我有一个指向对象的指针数组,其中每个指针都可能为nullptr,那么delete[]将不会删除这些指针所指向的对象,对吗?delete[]只会删除嵌入在数组中的数组元素。就像如果你有一个结构体数组,那么每个结构体的析构函数都会被调用。但如果你有一个指向结构体的指针数组,则不会调用任何结构体的析构函数。指针的内存将被释放,但是指向这些指针的任何结构体的内存都不会被释放。 - Shavais

36

这是C++中使用分配/释放模式的基本用法,包括malloc/freenew/deletenew[]/delete[]

我们需要相应地使用它们。但我想补充一下deletedelete[]之间的不同理解:

1)delete用于释放为单个对象分配的内存

2)delete[]用于释放为对象数组分配的内存

class ABC{}

ABC *ptr = new ABC[100]

当我们使用new ABC[100]时,编译器可以获取需要分配多少对象的信息(即100),并将为创建的每个对象调用构造函数。

但是,如果我们对于这种情况仅使用delete ptr,编译器将不知道指针ptr所指的对象有多少个,最终只会调用一个析构函数并删除一个对象的内存(留下了其他99个对象的析构函数的调用和释放)。因此,会出现内存泄漏。

因此,在这种情况下,我们需要使用delete [] ptr


5
这应该是正确的答案。其他答案中没有提到明显的区别:“但相应地,如果我们仅对该情况使用 delete ptr,编译器将不知道指针 ptr 指向多少个对象,并最终调用析构函数和删除仅一个对象的内存。” - Don Larynx
1
我们如何在C语言中实现相同的事情? - Dogus Ural
@DogusUral 为什么?C语言中没有析构函数,所以你只需要使用free()释放这个和那个。如果你使用伪析构函数模式,你需要使用一个for循环为每个对象调用一次。 - Kotauskas
@DonLarynx,正确的区别在于混淆它们会导致程序不完整。实现可能知道要销毁多少对象,也可能不知道。它允许知道调用方式错误,并中止程序告诉您问题所在。 - Caleth

9
当我提出这个问题时,我的真正问题是,“这两者之间有什么区别吗?运行时不必保留有关数组大小的信息,因此它将无法确定我们指的是哪一个?”这个问题在“相关问题”中没有出现,所以为了帮助像我一样的人,这里是答案:“为什么我们需要delete[]操作符?”。请注意,本文保留了HTML标签。

2
感谢您回来并将其添加进去。 - Ziffusion

6

deletedelete [] 操作符分别用于销毁使用 newnew[] 创建的对象,并将剩余的已分配内存返回给编译器的内存管理器。

使用 new 创建的对象必须使用 delete 销毁,而使用 new[] 创建的数组应该使用 delete[] 进行删除。


4

C++ delete[] 运算符可确保调用使用 new[] 分配的所有对象的析构函数。以下示例演示相同的内容。同时,如果类具有非默认析构函数以释放获取的资源,则必须首选使用 delete[](如果先前使用 new[])。否则,可能会导致内存泄漏。

常见代码:

#include <iostream>
using namespace std;

class memTest{
    public:
    static int num;
    memTest(){
cout<<"Constructor from object " << num++ << endl;
    }
    ~memTest(){
cout<<"Destructor from object " << --num << endl;        
    }
};
int memTest::num=0;

例如1:使用new[]和delete可能导致未定义行为。

int main() {
    memTest* Test1=new memTest[3];
    
    delete Test1; //<-----
    return 0;
} 

输出 1:-

Constructor from object 0
Constructor from object 1
Constructor from object 2
Destructor from object 2 //<-----

示例2:正确的做法是使用new[]和delete[]。

int main() {
    memTest* Test1=new memTest[3];
    
    delete[] Test1; //<-----
    return 0;
} 

输出2:

Constructor from object 0
Constructor from object 1
Constructor from object 2
Destructor from object 2
Destructor from object 1 //<-----
Destructor from object 0 //<-----

-1

delete 用于删除单个指针,而 delete[] 用于通过指针删除数组。 这里 可能会帮助您更好地理解。


1
链接已损坏。 - asynts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接