C++ 中的 delete vs delete[] 运算符

Posted

技术标签:

【中文标题】C++ 中的 delete vs delete[] 运算符【英文标题】:delete vs delete[] operators in C++ 【发布时间】:2022-01-11 22:33:43 【问题描述】:

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

【问题讨论】:

您可能会发现这个问题与***.com/questions/1913343/… 相关 delete 和 delete[] 的问题是我喜欢智能指针的原因之一,并且尽可能使用 vector<> 而不是数组。 ***.com/questions/1553382/… @DavidThornley 如果你使用智能指针,你仍然需要知道你仍然需要知道不要写的意义上的区别,例如std::unique_ptr<int>(new int[3]),因为它会在数组上调用常规的delete,这是未定义的行为。相反,您需要使用std::unique_ptr<int[]> @DavidThornley 当您事先知道大小时,您可能应该使用std::array,因为它在概念上更符合该用例并且很可能会更快启动。 【参考方案1】:

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

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

new [] 返回的指针上使用delete 或在new 返回的指针上使用delete [] 会导致未定义的行为。

【讨论】:

我想知道在 new[] 数组中使用 delete 是否会导致未定义的行为,例如 int 或 char(无构造函数/析构函数)。使用原始类型时,数组大小似乎没有存储在任何地方。 如果标准没有定义完成后会发生什么,则根据定义,它是“未定义的行为”,即使您的编译器确定性地执行您希望它执行的操作。另一个编译器可能会做一些完全不同的事情。 当我有一个像“char** strArray”这样的 C 字符串数组时,我犯了这个错误。如果您像我一样有一个数组,则需要遍历数组并删除/释放每个元素,然后删除/释放 strArray 本身。在我没有使用的数组上使用“delete[]”是行不通的,因为(正如上面的 cmets 和答案所指出的那样),它调用了 DESTRUCTORS,它实际上并没有释放每个插槽。【参考方案2】:

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)(例如存储元素的数量,因此稍后在删除时知道要完成多少次析构函数调用)。如果该类声明了一个operator new[],除了内存量之外还接受另一个size_t,那么第二个参数将接收分配的元素数量——它可以将其用于任何它想要的目的(调试等)。

对于创建非数组对象的new,它将在元素的类或全局范围内查找operator new。它传递了请求的内存量(总是sizeof(T))。

对于delete[],它查看数组的元素类类型并调用它们的析构函数。使用的operator delete[] 函数是元素类型类中的一个,或者如果没有,则在全局范围内。

对于delete,如果传递的指针是实际对象类型的基类,则基类必须具有虚拟析构函数(否则,行为未定义)。如果它不是基类,则调用该类的析构函数,并使用该类中的operator delete 或全局operator delete。如果传递了基类,则调用实际对象类型的析构函数,并使用在该类中找到的operator delete,或者如果没有,则调用全局operator delete。如果类中的operator delete 具有size_t 类型的第二个参数,它将接收要释放的元素数。

【讨论】:

如果我有一个指向对象的指针数组,每个指针都可能是 nullptr,delete[] 不会删除这些指针指向的对象,对吧? delete[] 只会删除物理嵌入数组中的数组元素。就像如果你有一个结构数组,那么每个结构析构函数都会被调用。但如果您有一个指向结构的指针数组,则不会。指针的内存将被释放,但不会释放这些指针指向的任何结构的内存。【参考方案3】:

这是 c++ 中 allocate/DE-allocate 模式的基本用法 malloc/free, new/delete, new[]/delete[]

我们需要相应地使用它们。但是我想对deletedelete[]之间的区别添加这个特殊的理解

1) delete 用于释放分配给单个对象

的内存

2) delete[] 用于释放分配给对象数组

的内存
class ABC

ABC *ptr = new ABC[100]

当我们说new ABC[100]时,编译器可以获得关于需要分配多少个对象(这里是100)的信息,并为每个创建的对象调用构造函数

但是相应地,如果我们在这种情况下简单地使用delete ptr,编译器将不知道ptr 指向多少个对象,最终将调用析构函数并删除仅1 个对象的内存(留下对析构函数的调用并释放剩余的 99 个对象)。因此会有内存泄漏。

所以在这种情况下我们需要使用delete [] ptr

【讨论】:

这应该是正确的答案。其他答案都没有提到明显的区别:“但相应地,如果我们在这种情况下简单地使用 delete ptr ,编译器将不知道 ptr 指向多少个对象,最终会调用析构函数并仅为 1 个对象删除内存” 我们如何在 C 中实现相同的目标? @DogusUral 为什么? C 中没有析构函数,所以你只需 free() 这个和那个。如果使用伪析构函数模式,则必须使用 for 循环为每个对象调用一次。 @DonLarynx 正确的区别在于将它们混合会导致程序格式错误。一个实现可能知道要销毁多少个对象,或者它可能不。允许它知道调用错误,并中止程序告诉你问题出在哪里。【参考方案4】:

deletedelete [] 运算符分别用于销毁使用newnew[] 创建的对象,返回分配给编译器内存管理器可用的内存。

new创建的对象必须用delete销毁,用new[]创建的数组必须用delete[]删除。

【讨论】:

【参考方案5】:

当我问这个问题时,我真正的问题是,“两者之间有区别吗?运行时是否不必保留有关数组大小的信息,因此它无法分辨我们指的是哪一个?”这个问题没有出现在“相关问题”中,所以只是为了帮助像我这样的人,这里是答案:"why do we even need the delete[] operator?"

【讨论】:

感谢您回来并把它放进去。【参考方案6】:

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

【讨论】:

链接已损坏。 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review

以上是关于C++ 中的 delete vs delete[] 运算符的主要内容,如果未能解决你的问题,请参考以下文章

深入了解C++中各种不同意义的new和delete

C++ 中的 new/delete 和 new[]/delete[]深入理解

C++ 中的 new/delete 和 new[]/delete[]深入理解

修正:C++编程经验:delete之后到底要不要置空?

delete[ ] 是不是会深度删除 C++ 中的结构?

C++中delete和delete[]的区别