什么时候最好使用堆栈而不是堆,反之亦然?

时间:2020-03-06 14:26:01  来源:igfitidea点击:

在C ++中,什么时候最好使用堆栈?什么时候最好使用堆?

解决方案

使用堆仅在运行时为对象分配空间。如果我们知道编译时的大小,请使用堆栈。与其从函数返回堆分配的对象,不如将缓冲区传递给函数以供其写入。这样,可以在将函数称为数组或者其他基于堆栈的结构的地方分配缓冲区。

我们拥有的malloc()语句越少,内存泄漏的机会就越少。

当前函数返回后将不使用变量时,请使用堆栈。在当前函数的生存期之外需要变量中的数据时,请使用堆。

这个问题与(虽然不是真正的骗子)与几天前问到的堆栈和堆在哪里以及什么有关。

这个问题病态严重。

在某些情况下,我们需要堆栈,其他情况下需要堆,其他情况下需要静态存储,其他情况下需要const内存数据,其他情况下需要免费存储。

堆栈速度很快,因为分配只是SP上的一个"增量",而所有"分配"都是在我们所使用的函数调用时执行的。堆(或者免费存储)分配/取消分配的时间更昂贵,并且容易出错。

根据经验,避免在堆栈上创建巨大的对象。

  • 在堆栈上创建对象可以使我们免于记住清理(读取删除)对象的负担。但是在堆栈上创建太多对象会增加堆栈溢出的机会。
  • 如果将堆用作对象,则将获得OS可以提供​​的尽可能多的内存,该内存要比堆栈大得多,但是,在完成后,必须再次确保释放内存。同样,在堆中创建太多的对象会导致内存碎片化,进而影响应用程序的性能。

当所使用的内存严格限于创建内存的范围时,请使用堆栈。这对于避免内存泄漏很有用,因为我们可以确切地知道要在哪里使用内存,并且知道何时不再需要它,以便为我们清除内存。

int main()
{ 
   if (...)
   {
      int i = 0;
   }
   // I know that i is no longer needed here, so declaring i in the above block 
   // limits the scope appropriately
}

但是,当可能在其创建范围之外访问内存并且我们不希望复制堆栈变量时,该堆很有用。这可以使我们明确控制如何分配和释放内存。

Object* CreateObject();

int main()
{
    Object* obj = CreateObject();
    // I can continue to manipulate object and I decide when I'm done with it

    // ..
    // I'm done
    delete obj;
    // .. keep going if you wish
    return 0;
}

Object* CreateObject()
{
   Object* returnValue = new Object();
   // ... do a bunch of stuff to returnValue
   return returnValue;
   // Note the object created via new here doesn't go away, its passed back using 
   // a pointer
}

显然,这里的一个常见问题是我们可能忘记删除对象。这称为内存泄漏。随着程序变得越来越琐碎,"所有权"(或者确切负责删除内容的人)变得更加难以定义,这些问题变得越来越普遍。

使用更多托管语言(C#,Java)的常见解决方案是实现垃圾回收,因此我们不必考虑删除内容。但是,这意味着在后台有一些东西会不定期地运行以检查堆数据。在非平凡的程序中,这会变得非常低效,因为"垃圾回收"线程弹出并弹出,寻找应删除的数据,而程序的其余部分则无法执行。

在C ++中,处理内存泄漏的最常见也是最好的解决方案是使用智能指针。其中最常见的是boost :: shared_ptr,它是(引用计数)

所以要重新创建上面的例子
boost :: shared_ptr CreateObject();

int main()
{
    boost::shared_ptr<Object> obj = CreateObject();
    // I can continue to manipulate object and I decide when I'm done with it

    // ..
    // I'm done, manually delete
    obj.reset(NULL);
    // .. keep going if you wish
    // here, if you forget to delete obj, the shared_ptr's destructor will note
    // that if no other shared_ptr's point to this memory 
    // it will automatically get deleted.
    return 0;
}

boost::shared_ptr<Object> CreateObject()
{
   boost::shared_ptr<Object> returnValue(new Object());
   // ... do a bunch of stuff to returnValue
   return returnValue;
   // Note the object created via new here doesn't go away, its passed back to 
   // the receiving shared_ptr, shared_ptr knows that another reference exists
   // to this memory, so it shouldn't delete the memory
}

作为经验法则,请尽可能使用堆栈。即当该范围之外永远不需要该变量时。

它的速度更快,导致碎片减少,并且将避免与调用malloc或者new相关的其他开销。从堆栈中分配是几个汇编程序操作,malloc或者new是有效实现中的数百行代码。

永远不要使用堆...这是不可避免的。 :)

上面提到的规则的一个例外情况是,通常应将堆栈用于函数范围之外不需要的局部变量:

如果递归函数分配了较大的局部变量或者被递归调用多次,则会耗尽堆栈空间。如果我们有一个利用内存的递归函数,则最好使用基于堆的内存而不是基于堆栈的内存。