为什么当我通过语句释放 Map 的内存时会发生此错误'delete'?

why this error happens when I release memory for map through 'delete' statement?

本文关键字:错误 delete 语句 释放 内存 Map 为什么      更新时间:2023-10-16

在下面的程序中,我尝试为std::map对象释放内存。

#include <map>
#include <iostream>
#include <unistd.h>
int main() {
  // std::map<int, int> *mp = new std::map<int, int>;;
  std::map<int, int> mp;
  for(int i = 0; i < 999999; i++){
    // mp->insert(std::pair<int, int>(i, 999999-i )); 
    mp[i] = 999999-i;
  }
  delete &mp;
  // delete mp;
  pause();
}

两个版本的程序(带有注释或取消注释当前注释并注释相应的行)都编译得很好,但是当我试图通过delete &mp释放内存时(也就是说,mp是std::map本身,而不是指向std::map的指针),它告诉我这个奇怪的错误消息:

test14_1(47555,0x7fff75c6f300) malloc: *** error for object 0x7fff5c601918: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

有人知道为什么会这样吗?

在你的程序中有两个方面。

::map

通过像以前那样声明变量,您在函数main()的作用域中创建了一个::map对象。映射在该函数的堆栈上分配,并在该函数(即函数作用域{ .. })进入时自动初始化。

释放::map

因为map是在进入作用域时在堆栈上分配的,所以当函数返回时(即函数作用域关闭),它会被自动删除。你不必担心这个。

通过调用delete操作符,您试图从进程中释放内存。delete操作符将指向已分配内存的指针作为参数,通过写入&mp,您正在创建这样一个指针。但是,由于mp对象位于分配它的堆栈上,因此c++运行时给您的错误是pointer being freed was not allocated在使用new操作符的堆上。

有趣的是,你的程序中已经有了正确的代码,你只是将分配/free错误地配对:
// Allocate the memory on the heap, and free it. 
// mp is a pointer to the allocated map.
int main() {
    std::map<int, int> *mp = new std::map<int, int>;
    // do stuff, e.g. mp->insert()
    delete mp;
}
// Allocate the memory on the stack, freed automatically.
// mp is a reference to the allocated map.
int main() {
    std::map<int, int> mp;
    // do stuff, e.g. mp.insert()
    // no need to free mp explicitly!
}

注意

你可能想了解C/c++程序如何组织它们的内存:堆和堆栈内存分配(看看这里或这里),以及指针和内存分配的引用之间的区别(看看这里或这里)。

PS: 你很幸运看到错误信息。在禁用内存安全的优化代码中(例如gcc的内存消毒器),程序的行为将不确定,可能会崩溃。

您不需要删除std::map,因为您没有使用new操作符分配内存。所有标准容器(如std::map)都能够自己管理它们的内存。他们内部使用allocator进行内存管理。

使用

  std::map<int, int> mp;
  delete &mp;

类似于:

  int i;
  delete &i;

是错误的

只有在使用new分配对象时,才需要使用delete来释放内存。

在堆栈上创建的对象将自动删除。如果类的析构函数做了正确的事情,客户机代码就没有更多的事情要做了。在std::map的情况下,析构函数将执行必要的解分配。你的函数不需要做任何事情

这是基本的RAII。

任何堆栈局部对象,包括int/float等类型的简单变量,或者在你的例子中是容器,在作用域结束时被销毁。

对于显式销毁,

{
   vector<int> v1;
   map<int,int> m1;
   delete &m1;
} // implicit destruction of v1 and m1!

使用显式delete语句,结果代码将最终释放已经释放的内存。