none
用new T[]分配的内存为什么不能用循环和delete来删除? RRS feed

  • 问题

  • //以下代码在机器上编译通过,但出现运行时错误,请帮助解释:
    int*k=new int[5];
    for(int counter=0;counter<5;++counter)
        delete (k+counter);

    ///请各位大师指教

    2009年11月19日 14:27

答案

  • 蒋老师说的是正确的对于new []如果使用delete来释放的在C++标准中的描述是未定义的。
    之所以你这个循环运行时会出错,是编译器的行为。具体行为我理解如下:
    关键在于你创建的对象是简单数据类型。int类型具有bitwise语义。
    这时候delete并不是按照对象释放的过程执行,循环调用各个对象的析构函数,然后释放内存空间。而是直接根据k指针前面4字节所确定的指针所指向的内存大小。直接释放该位置的内存。
    现在让我们来看看你的代码
    首先你创建了一个长度为5的int数组。此时指针k前面的4字节应该记录着这个空间大小。下面开始你的循环
    第一次:delete (k+0)这个语句根据我上面的解释,则会释放整个数组的内存。
    第一次以后:由于k+conter所指向的地址空间已经释放了,所以再去delete就会产生错误。

    你可以做这样的例子
    char* k = new char[10485760];
    这个大小是10mb内存,你可以观察TaskManager的内存使用情况。
    delete k;
    这时候再看看内存情况。
    总而言之这样的行为是由编译器自行决定的。不要尝试标准以外的行为。

    麻烦把正确答案设为解答。
    • 已标记为答案 LinZhe Li 2009年11月20日 5:39
    2009年11月20日 1:12
    版主
  • 1当你new出一个数组的时候,系统会在你指针指向的地址前占用一个size_t大小的空间。用来指定数组长度。在delete的时候,会根据这个值来释放数组空间。
    2你的自定义类型也可能是具有bitwise语义的。如果你的类型中没有自定义构造函数。没有虚继承。继承来的基类也满足前面两条,成员变量也满足前面两条,则该类型具有bitwise语义。如果一个类型具有bitwise语义,则系统不会产生默认构造函数、析构函数等等。通过new创建对象自然不会调用构造函数,delete也不会调用析构函数。
    麻烦把正确答案设为解答。
    • 已标记为答案 LinZhe Li 2009年11月20日 5:39
    2009年11月20日 5:28
    版主

全部回复

  • C++语言规范中没有说你可以这么做。所以delete非new出来的地址会有不可预料的结果。

    The following is signature, not part of post
    Please mark the post answered your question as the answer, and mark other helpful posts as helpful.
    Visual C++ MVP
    2009年11月19日 22:00
    版主
  • 蒋老师说的是正确的对于new []如果使用delete来释放的在C++标准中的描述是未定义的。
    之所以你这个循环运行时会出错,是编译器的行为。具体行为我理解如下:
    关键在于你创建的对象是简单数据类型。int类型具有bitwise语义。
    这时候delete并不是按照对象释放的过程执行,循环调用各个对象的析构函数,然后释放内存空间。而是直接根据k指针前面4字节所确定的指针所指向的内存大小。直接释放该位置的内存。
    现在让我们来看看你的代码
    首先你创建了一个长度为5的int数组。此时指针k前面的4字节应该记录着这个空间大小。下面开始你的循环
    第一次:delete (k+0)这个语句根据我上面的解释,则会释放整个数组的内存。
    第一次以后:由于k+conter所指向的地址空间已经释放了,所以再去delete就会产生错误。

    你可以做这样的例子
    char* k = new char[10485760];
    这个大小是10mb内存,你可以观察TaskManager的内存使用情况。
    delete k;
    这时候再看看内存情况。
    总而言之这样的行为是由编译器自行决定的。不要尝试标准以外的行为。

    麻烦把正确答案设为解答。
    • 已标记为答案 LinZhe Li 2009年11月20日 5:39
    2009年11月20日 1:12
    版主
  • 蒋老师说的是正确的对于new []如果使用delete来释放的在C++标准中的描述是未定义的。
    之所以你这个循环运行时会出错,是编译器的行为。具体行为我理解如下:
    关键在于你创建的对象是简单数据类型。int类型具有bitwise语义。
    这时候delete并不是按照对象释放的过程执行,循环调用各个对象的析构函数,然后释放内存空间。而是直接根据k指针前面4字节所确定的指针所指向的内存大小。直接释放该位置的内存。
    现在让我们来看看你的代码
    首先你创建了一个长度为5的int数组。此时指针k前面的4字节应该记录着这个空间大小。下面开始你的循环
    第一次:delete (k+0)这个语句根据我上面的解释,则会释放整个数组的内存。
    第一次以后:由于k+conter所指向的地址空间已经释放了,所以再去delete就会产生错误。

    你可以做这样的例子
    char* k = new char[10485760];
    这个大小是10mb内存,你可以观察TaskManager的内存使用情况。
    delete k;
    这时候再看看内存情况。
    总而言之这样的行为是由编译器自行决定的。不要尝试标准以外的行为。

    麻烦把正确答案设为解答。
    您好,谢谢您的回复
    恩,您解释的很清楚,但我还是想更进一步的询问一些问题。
    1、“k指针前面4字节所确定的指针所指向的内存大小”指的是什么?恩,我的理解是指针定义时的类型,但类型指出是int*,即指向区域为int大小的空间,那么用delete删除的时候应该是删除一个元素,但正如您给出的例子,所有分配的内存都被删除了。
    2、我声明了一个对象,再用new[]分配了空间,但删除的时候依然是全部删除。正如您和蒋老师所说,这取决于编译器的行为,那么可否解释一下您回复中的
    关键在于你创建的对象是简单数据类型。int类型具有bitwise语义。
    这时候delete并不是按照对象释放的过程执行,循环调用各个对象的析构函数,然后释放内存空间。而是直接根据k指针前面4字节所确定的指针所指向的内存大小。直接释放该位置的内存。
    我对此并不是太理解。
    再次谢谢您的殷勤回复,以及蒋老师的解答。我想我确实应该好好看看标准了。谢谢。

    2009年11月20日 4:28
  • 1当你new出一个数组的时候,系统会在你指针指向的地址前占用一个size_t大小的空间。用来指定数组长度。在delete的时候,会根据这个值来释放数组空间。
    2你的自定义类型也可能是具有bitwise语义的。如果你的类型中没有自定义构造函数。没有虚继承。继承来的基类也满足前面两条,成员变量也满足前面两条,则该类型具有bitwise语义。如果一个类型具有bitwise语义,则系统不会产生默认构造函数、析构函数等等。通过new创建对象自然不会调用构造函数,delete也不会调用析构函数。
    麻烦把正确答案设为解答。
    • 已标记为答案 LinZhe Li 2009年11月20日 5:39
    2009年11月20日 5:28
    版主
  • 确实如此。
    谢谢您的回答。
    一下程序在我计算机上刚刚开始占用698044k,再占用就只占用了657004k了。
    在其中加析构函数delete (ss)会报错(如注释掉的那句语句),应该也是类似原因吧?试图释放在析构函数中已经被释放的内存?
    #include<iostream>
    using namespace std;
    class mm
    {
    public:
     mm(){ss=new char;}
     char*ss;
    // ~mm(){delete(ss);}
    };
    int main()
    {
    mm * k = new mm[10485760];
    cin.get();
    cout<<sizeof(int*)<<endl;
    delete k;
    cin.get();
    }
    2009年11月20日 5:49
  • 未定义行为。不要尝试了。


    麻烦把正确答案设为解答。
    2009年11月20日 8:37
    版主