避免堆内存破坏

4
今天在EFNet C++ Wiki的堆栈破坏文章中,我发现了两段代码。
void this_is_bad() /* You wouldn't believe how often this kind of code can be found */    
{    
    char *p = new char[5];    /* spend some cycles in the memory manager */    
    /* do some stuff with p */    
    delete[] p;      /* spend some more cycles, and create an opportunity for a leak */    
 }  

替代方法:

void this_is_good()    
{    
   /* Avoid allocation of small temporary objects on the heap*/   
   char p[5];    /* Use the stack instead */   
   /* do some stuff */  
}    

有人可以帮我理解为什么第一段代码被认为不好吗?


1
第一个代码片段中写评论的人让我觉得不够专业。在堆栈上分配5个字节通常比在堆上更有意义,而且异常安全性也很好,但是这些观点可以用更少的轻率言辞来表达得更好(以“你不会相信”开头的话语是一个不好的迹象)。 - asveikau
顺便说一下,根据评论搜索结果看来这不是维基百科,而是这个网站:http://www.efnetcpp.org/wiki/Heap_Corruption。 - asveikau
我的错,实际上我很匆忙。我看了维基百科。 - Green goblin
4个回答

7
当使用char* p时,您正在堆上分配p,因此必须在最后删除p。在char *pdelete 之间,在执行一些p操作的代码中,代码可能会引发异常,导致p泄漏。
当使用char p [5]时,您正在栈上分配p,这样您就不必担心delete,即使代码引发异常,您也是安全的。
void this_is_bad()   
{    
  char *p = new char[5]; //on the heap
  // What happens if I throw an unhandled exception here?
  delete[] p;  // I never get to delete p and it gets leaked
}  

2
评论也试图传达newdelete不是零成本操作。而在堆栈上分配数组通常意味着从堆栈指针寄存器中减去,这非常快速。 - asveikau

2
当您使用堆栈而不是堆时,一旦当前函数的作用域丢失,内存就会被恢复。
当您使用“new”关键字时,您分配堆内存。您必须记住删除使用“new”关键字分配的任何内存。如果在“new”关键字之后和“delete”关键字之前抛出异常,则可能会创建内存泄漏,因为您可能无法在异常抛出后恢复执行到抛出异常的点之后。

2
堆是共享内存资源,必须由进程外部进行管理(如示例中所述的内存管理器)。另一方面,栈由进程管理,您方法中推入栈的任何变量都会在方法完成时自动释放/弹出。这样做既干净,几乎不需要代价,也几乎不会出错。
这避免了创建内存泄漏的可能性——即通过“delete”传递的区域(例如,通过错误地重新分配ptr值)与其通过“new”操作分配的内存不完全匹配。在方法中使用非静态变量时没有理由采用其他方法。
另请参见: C ++,Free-Store vs Heap

2
目前最好的方法是:
 #include <vector>

 void this_is_great()
 {
     std::vector<char> myCharVec(5);

     // use myCharVec

 }  // when this function ends whether by return or by exception myCharVec is cleaned up

这种方式使得向量(类似于“数组”)中的内存位于堆上,但对象存在并且一些簿记在栈上(粗略地说),当对象被析构时,它的堆内存会由向量的析构函数自动清理,没有垃圾回收开销等。

这就是所谓的RAII习惯用法

之所以比将其直接放在栈上更受欢迎的另一个原因是,堆栈上的缓冲区溢出(在处理数组时经常发生)可能比内存位于堆上更具破坏性和难以检测。


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