【问题标题】:Can I use `operator delete[]` for a single element array allocation?我可以使用 `operator delete[]` 进行单元素数组分配吗?
【发布时间】:2013-07-12 07:23:51
【问题描述】:

如果我们分配一个大小为 1 的对象,如下所示

int *arr = new int[1];

我们应该使用operator delete[]operator delete 删除对象吗?

我担心的原因是编译器是否足够聪明,可以将语句转换为单个元素分配int *arr = new int,这会导致调用operator delete[] UB。

用户案例:

我有一个指针,我最终会以多种方式分配它,但最终希望将其删除。所以想知道,对于单元素分配,如果我一直使用int *arr = new int[1],我是否可以一直安全地使用operator delete[]

注意

您能否请我参考标准以支持您的回答?

【问题讨论】:

  • operator delete[] 当然。 int[1] 不是 int。编译器足够智能不会转换语句。
  • 标准中的相同位置告诉您在new[] 之后调用delete[]。尺寸 1 没有特殊情况。
  • 如果编译器足够聪明地做到这一点(尽管将一种类型更改为另一种与智能完全没有任何关系),它必须通知您此更改,或者必须在标准中指定此更改,在这种情况下,它也不是聪明的,而是强制性的。如果它默默地在背后,那么那个编译器并不聪明,而是故意邪恶。除了常识之外,不需要任何标准文档,int 只是 int[1] 不同。单元素数组仍然是数组吗(提示:答案隐藏在此评论中)?
  • 可能还值得指出的是,几乎没有已知的案例应该使用new[]
  • 这是一个愚蠢的问题。编译器有多“聪明”并不重要,它不能引入未定义的行为(1.9 [intro.execution] p5)并要求程序员更改正确的源代码,并且您已经知道将delete[]与@一起使用987654337@ 是正确的并且是标准要求的。 “智能”编译器的这种优化意味着程序可以在一个编译器上运行,而不是在另一个编译器上运行。那就是疯狂所在。

标签: c++


【解决方案1】:

您必须使用delete[] 而不是delete。不允许编译器将new int[1]更改为new int

(由于int 是一种 POD 类型,new intnew int[1] 很可能在幕后做同样的事情,但如果是这种情况,那么 delete[]int* 和 @987654330 @@int* 也会做同样的事情。)

ISO/IEC 14882:2011 5.3.5 [expr.delete] / 2:

在第一种选择中(删除对象),delete的操作数的值可能是一个空指针值,一个指向前一个创建的非数组对象的指针new-expression,或指向表示此类对象的基类的子对象(1.8)的指针(第 10 条)。如果不是,则行为未定义。

由于int[1] 是一个数组对象,如果您尝试使用delete 而不是delete[] 删除它,则行为未定义。

【讨论】:

  • 能否请您参考标准?
  • @Abhijit:标准中没有规定,所以我不能给你一个节号,但同样的原因是不允许的。
  • 除了 delete-expression 限制之外,标准库释放函数也有限制:“否则,如果提供给 operator delete(void*) 的值在标准库不是之前在标准库中调用 operator new(std::size_t) 或运算符 new(std::size_t, const std::nothrow_t&) 返回的值之一 [...]"(+ 数组 (de)alloc 相同)[basic.stc .dynamic.deallocation]/3
【解决方案2】:

规则很简单:

  1. 始终平衡newdelete
  2. 始终平衡new[]delete[]

否则你会得到未定义的行为

没有例外;即使new[1] 必须与delete[] 平衡,并且new[0] 必须删除,因为编译器仍然可以保留存储空间。

【讨论】:

  • IIRC, new[0] 是不允许的。
  • 如果我没记错的话。不过,我没有从快速标准扫描中看到任何东西,所以也许我记错了,或者遇到了错误的编译器错误,或者我正在考虑一个正常的数组。
  • 我记得几年前错误地分配了 new[0]。但是那个编译器甚至没有正确地确定 for 循环的范围!我今天正在升级我的编译器(到 C++11):将重新检查。
  • 它对我来说似乎以任何形式都可以正常工作。我真的认为这是我在考虑的堆栈分配数组。
【解决方案3】:

我们应该使用运算符delete[]operator delete 删除对象吗?

operator delete[]。您已经分配了一个ints 的数组,因此没有其他选项可以正确地释放它。使用 operator delete 会调用未定义的行为。

【讨论】:

    【解决方案4】:

    您必须使用delete[]

    C++11 5.3.5 删除

    ::opt 删除强制转换表达式

    ::opt delete [ ] 转换表达式

    第一种是非数组对象,第二种是数组。

    一个包含一个元素的数组仍然是一个数组。

    【讨论】:

      【解决方案5】:
      int *arr = new int[1];
      

      由于您使用[] 进行分配,因此您也需要使用[] 分配delete

      Memory leaks

      由新表达式创建的对象(具有动态存储的对象 持续时间)持续到新表达式返回的指针是 在匹配删除表达式中使用。

      所以你应该使用delete[] 来释放内存,否则你会得到未定义的行为。

      以及(incase),如果编译器足够聪明,可以将int * arr = new int[1] 视为int,那么它必须足够聪明,将arr 上的delete[] arr 视为delete arr但是delete 无法区分 arrint*arr = new int[1]int*arr = new int[] in delete 将表明它。

      【讨论】:

        【解决方案6】:

        不,它不安全,它可能会导致一些不必要的情况。
        例如,内存中可能在指针指向的位置旁边有一些数据(int/char/float...)。那时如果你使用了delete [],那么如果它是一个整数,它也可能会尝试删除该数据,对于其他数据类型它会导致意外错误。

        示例:

        int *ptr = new int;
        int a = 20;
        char c = 'e';
        *ptr = 10;
        delete [] ptr;
        

        ca 变量有可能存储在指针ptr 指向的位置旁边,如果a 存储在它旁边,那么它也会删除“a” ,如果存在其他数据类型,则会导致意外的运行时结果。

        因此建议使用delete[] 仅用于删除使用新数据类型分配的内存[]。希望这很有用。

        【讨论】:

        • 堆栈分配的变量如何在堆分配的变量旁边结束?你是怎么想到这个“可能性”的?请不要仅仅为了写作而写作。
        猜你喜欢
        • 1970-01-01
        • 2019-09-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-01
        • 1970-01-01
        • 2021-01-14
        • 1970-01-01
        相关资源
        最近更新 更多