C++ delete[] on new char;

5

Consider the following code:

int main()
{
    char* str = new char; 
    str[0] = 'a'; 
    delete[] str; //Notice the []
}

它能够编译、运行且没有崩溃(VC15和g++)。

但是,我可以通过valgrind清晰地看到1个内存泄漏。

然而,如果我运行以下代码:

#include <iostream>

class Foo{
public:
    Foo(){
        std::cout << "Foo::Foo" << std::endl;
    }
    ~Foo(){
        std::cout << "Foo::~Foo" << std::endl;
    }
};

int main()
{   
      Foo* foo = new Foo;
      delete[] foo;
      return 0;
}

在Windows上运行时,我遇到了析构函数调用的无限循环,在Linux上则出现了无效指针错误(在约20个析构函数调用后)。

我不能理解为什么这两者会有差异?为什么使用new char不会导致无限循环或崩溃?


3
由于未定义行为,你不能期望从存在此类缺陷的代码中得到特定的结果。 - juanchopanza
什么被定义为“未定义”?指针上的delete[]操作? - ZivS
2
是的,在未使用new[]分配的内容上调用delete[] - juanchopanza
我不知道这是未定义的,谢谢。 - ZivS
众所周知,MSVC恰好会为像char这样的内置类型生成完全相同的代码,用于deletedelete[]。对于具有析构函数的类型,代码则非常不同。 - Bo Persson
2个回答

7
您应该只使用 new[] 分配的指针来使用 delete[];

问题

在您的第一个代码片段中:

  • 通过语句 char* str = new char;,您获得了一个指向单个字符的指针。因此,new 的类型与 char* 匹配。
  • 但是您的 delete[] 期望指向字符数组的指针,因此当您删除不是数组的内容时,会出现未定义行为(UB)。

第二个代码片段也有完全相同的问题。 UB 是未定义的,可能会产生奇怪和无法解释的结果。

如何解决?

要么为单个元素分配使用单个元素删除:

  Foo* foo = new Foo;
  delete foo;

或者使用数组删除来进行数组分配:

  Foo* foo = new Foo[1];
  delete[] foo;

或者更好的方法是,摆脱你想使用的任何new/delete,并使用向量代替数组。

@ZivS 使用 delete 而不是 delete[]。 - Lorence Hernandez

2
简单来说,对于一个没有使用new[]分配内存且不为null的指针调用delete[]总是未定义的
我引用自cppreference.com

delete[]表达式调用以释放之前为对象数组分配的存储空间。除非ptr是null指针或者是之前从标准库实现的operator new或operator new[](size_t,std::nothrow_t)获得的指针,否则标准库实现此函数的行为是未定义的


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