delete(非数组形式)是否知道new或new[]分配的内存总量

Does delete (non array form) know the total amount of memory allocated by either new or new[]

本文关键字:new 分配 内存 数组 是否 delete      更新时间:2023-10-16

这个问题是在调用析构函数后是否删除[]一次性释放内存?但作为一个单独的问题离开了。

似乎(如果错误,请纠正我(deletedelete[]之间的唯一区别是delete[]将获取数组大小信息并调用所有数组的析构函数,而delete将析构函数中唯一的第一个。特别地,delete还可以访问关于new[]分配了多少总内存的信息。

如果不关心破坏动态分配的数组元素,而只关心由newnew[]分配的内存被释放,那么delete似乎能够做同样的工作。

This How do delete[]";知道";操作数数组的大小?这个问题的公认答案有一条来自@AnT的评论,我引用

还要注意,数组元素计数器仅用于具有非平凡析构函数的类型。对于具有琐碎析构函数的类型,计数器不是由new[]存储的,当然也不是由delete[]检索的

这条注释表明,通常delete表达式知道分配的整个内存量,因此知道最终在一次释放多少内存,即使内存包含一个元素数组。所以如果有人写

auto pi = new int[10];
...
delete pi;

尽管标准认为这是UB,但在大多数实现中,这不应该泄露内存(尽管它不是可移植的(,对吧?

在C++标准下,在分配了new[]的东西上调用delete只是未定义的行为,在分配有new的东西上呼叫delete[]也是如此。

在实践中,new[]将通过类似malloc的东西来分配内存,new也是如此。delete将销毁指向的对象,然后将内存发送到类似free的对象。delete[]将销毁数组中的所有对象,然后将内存发送给类似free的对象。new[]可以分配一些额外的存储器以传递给delete[],从而给delete[]要销毁或不销毁的元素的数量。

如果使用实际的malloc/free,那么一些实现将允许使用指向malloc块中任何位置的指针。其他人不会。需要将与从malloc获得的值完全相同的值传递给free才能定义该值。这里有一个问题,如果new[]为数组大小/元素步长留出了一些额外的空间,并将其固定在块之前,则delete将向第一个元素传递指针,然后delete将向free传递与从malloc获得的new[]不同的指针。(我认为有一种架构会发生这样的事情。(

与大多数未定义的行为一样,您不能再依赖于审核您编写的代码,而是现在致力于审核生成的程序集与您交互的C/C++标准库,然后才能确定要执行的行为是否正确。在实践中,这是一个无法实现的负担,因此您的代码最终会具有负值,即使您在实际检查时检查了事情的工作方式。如何确保每次更改编译器版本、标准库版本、操作系统版本、系统库或编译器时都会进行相同的检查(对生成的二进制文件及其行为(?

这是正确的。delete和delete[]的区别在于,后者知道数组中分配的项数,并对其上的每个对象调用析构函数。为了100%正确,两者实际上都"知道"它——为数组分配的项数等于分配的内存大小(两者都知道(除以对象的大小。

有人可能会问,为什么我们需要delete[]和delete than——为什么delete不能执行相同的计算?答案是多态性。当通过指向基类的指针进行删除时,分配的内存大小将不等于静态对象的大小。

另一方面,delete[]没有考虑对象被多态化的可能性,这就是为什么动态数组永远不应该被视为多态对象(即分配和存储为基类的指针(。

对于泄漏内存,在阵列上使用POD类型的情况下,delete不会泄漏内存。

避免所有构造引发未定义行为的一个具体原因是,编译器有权假设从未发生过未定义行为,即使您看不出它们可能会出错。例如,给定这个程序。。。

#include <iostream>
#include <cstring>
int main(int argc, char **argv)
{
    if (argc > 0) {
        size_t *x = new size_t[argc];
        for (int i = 0; i < argc; i++)
            x[i] = std::strlen(argv[i]);
        std::cout << x[0] << 'n';
        delete x;
    }
    return 0;
}

编译器可能会发出与…相同的机器代码。。。

int main(void) { return 0; }

因为CCD_ 35控制路径上的未定义行为意味着编译器可能认为该路径从未被占用。