关于c ++:什么时候最好使用堆栈而不是堆,反之亦然?


When is it best to use the stack instead of the heap and vice versa?


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



在当前函数返回后不使用变量时使用堆栈。当需要超出当前函数生命周期的变量中的数据时使用堆。



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

  • 在堆栈上创建对象可以免除记忆清除(读取删除)对象的负担。但是在堆栈上创建太多对象会增加堆栈溢出的可能性。
  • 如果你使用堆作为对象,你可以获得操作系统可以提供的内存,比堆栈大得多,但是你必须确保在完成后释放内存。此外,在堆中过于频繁地创建太多对象会使内存碎片化,从而影响应用程序的性能。



当正在使用的内存严格限制在您创建它的范围内时,请使用堆栈。这对于避免内存泄漏非常有用,因为您确切知道要使用内存的确切位置,并且知道何时不再需要内存,因此将为您清理内存。

1
2
3
4
5
6
7
8
9
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
}


但是,当您的内存可以在其创建范围之外访问并且您不希望复制堆栈变量时,堆非常有用。这可以让您明确控制内存的分配和释放方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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是高效实现中的几百行代码。


它永远不会最好使用堆...只是不可避免。 :)



这个问题与几天前被问到的堆栈和堆的内容和位置有关(虽然不是真正的愚蠢)。



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


您拥有的malloc()语句越少,内存泄漏的可能性就越小。



问题形成不良。


在某些情况下,您需要堆栈,其他需要堆的地方,其他需要静态存储的地方,其他需要const内存数据的地方,其他需要免费商店的地方。


堆栈很快,因为分配只是SP上的"增量",并且所有"分配"都是在您所在的函数的调用时执行的。堆(或免费存储)分配/解除分配更耗时且容易出错。