19

一位经验丰富的 C++ 用户告诉我,我应该努力使用堆变量,即:

A* obj = new A("A");

相对于:

A obj("A");

除了所有关于使用指针的美好和灵活的东西之外,他说最好把东西放在堆上而不是堆栈上(堆栈比堆小吗?)。这是真的吗?如果是,为什么?

注意:我知道关于寿命的问题。假设我已经适当地管理了这些变量的生命周期。(即唯一关注的标准是堆与堆栈存储,没有生命周期问题)

4

8 回答 8

15

根据上下文,我们可以考虑堆或堆栈。每个线程都有一个堆栈,线程通过调用函数来执行指令。当一个函数被调用时,函数变量被压入堆栈。并且当函数返回堆栈回滚并回收内存时。现在线程本地堆栈有一个大小限制,它会有所不同并且可以在一定程度上进行调整。考虑到这一点,如果每个对象都是在堆栈上创建的,并且该对象需要大量内存,那么堆栈空间将耗尽,从而导致 stackoverflow 错误。除此之外,如果要由多个线程访问该对象,则将此类对象存储在堆栈上是没有意义的。

因此,小变量、小对象的大小可以在编译时确定,指针应该存储在堆栈上。将对象存储在堆或自由存储上的问题是,内存管理变得困难。有内存泄漏的机会,这很糟糕。此外,如果应用程序尝试访问已被删除的对象,则可能会发生访问冲突,从而导致应用程序崩溃。

C++11 引入了智能指针(共享、唯一),使堆内存管理更容易。实际引用的对象在堆上,但被始终在堆栈上的智能指针封装。因此,当在函数返回事件或异常期间堆栈回滚时,智能指针的析构函数会删除堆上的实际对象。在共享指针的情况下,引用计数保持不变,当引用计数为零时,实际对象被删除。 http://en.wikipedia.org/wiki/Smart_pointer

于 2012-06-02T08:14:30.190 回答
9

没有关于使用堆栈分配变量和堆分配变量的一般规则。只有指导方针,这取决于您要做什么。

以下是一些优点和缺点:

堆分配:

优点:

  • 更灵活 - 如果您有很多在编译时不可用的信息
  • 更大 - 你可以分配更多 - 但是,它不是无限的,所以在某些时候如果分配/解除分配没有正确处理,你的程序可能会耗尽内存

缺点:

  • 较慢 - 动态分配通常比堆栈分配慢
  • 可能会导致内存碎片 - 分配和释放不同大小的对象会使内存看起来像瑞士奶酪 :) 如果没有所需大小的可用内存块,则会导致某些分配失败
  • 更难维护 - 正如您所知,每个动态分配之后必须有一个释放,这应该由用户完成 - 这很容易出错,因为在很多情况下人们忘记将每个 malloc() 调用与 free( ) 调用或使用 delete() 调用 new()

堆栈分配:

优点:

  • 更快 - 这主要在嵌入式系统上很重要(我相信对于嵌入式系统有一个禁止动态分配的 MISRA 规则)
  • 不会造成内存碎片
  • 使应用程序的行为更具确定性 - 例如,消除了在某些时候耗尽内存的可能性
  • 不易出错 - 因为不需要用户处理释放

缺点:

  • 不太灵活 - 您必须在编译时提供所有信息(数据大小、数据结构等)
  • 尺寸更小 - 但是有一些方法可以计算应用程序的总堆栈大小,因此可以避免堆栈不足

我认为这抓住了一些优点和缺点。我敢肯定还有更多。

最后,这取决于您的应用程序需要什么。

于 2012-06-02T08:52:55.250 回答
8

堆栈应该优先于堆,因为堆栈分配的变量是自动变量:当程序脱离其上下文时,它们的销毁会自动完成。

事实上,在栈和堆上创建的对象的生命周期是不同的:

  • 函数或代码块的局部变量{}(不是由 new 分配的)位于堆栈上。当您从函数返回时,它们会自动销毁。(调用它们的析构函数并释放它们的内存)。
  • 但是,如果您需要在函数之外使用某个对象,则必须在堆中分配(使用 new)或返回一个副本。

例子:

 void myFun()
 {
   A onStack; // On the stack
   A* onHeap = new A(); // On the heap
   // Do things...

 } // End of the function onStack is destroyed, but the &onHeap is still alive

在此示例中,onHeap函数结束时仍会分配其内存。这样,如果您没有指向onHeap某处的指针,您将无法删除它并释放内存。这是内存泄漏,因为内存将丢失,直到程序结束。

但是,如果您要返回一个指针 on onStack,因为onStack在退出函数时被破坏,使用该指针可能会导致未定义的行为。虽然使用onHeap仍然完全有效。

为了更好地了解堆栈变量是如何工作的,您应该搜索有关调用堆栈的信息,例如Wikipedia 上的这篇文章。它解释了如何堆叠变量以在函数中使用。

于 2012-06-02T07:18:07.973 回答
4

在 C++ 中尽可能避免使用new总是更好的。
但是,有时您无法避免它。
例如:
希望变量存在于其范围之外。

所以它应该是真正的课程,但如果你有选择,总是避免堆分配变量。

于 2012-06-02T06:27:22.757 回答
1

答案并不像某些人让你相信的那样明确。

通常,您应该更喜欢自动变量(在堆栈上),因为它更简单。然而,有些情况需要动态分配(在堆上):

  • 编译时未知大小
  • 可扩展(容器内部使用堆分配)
  • 大物体

后者有点棘手。理论上,自动变量可以无限分配,但计算机是有限的,更糟糕​​的是,大多数时候堆栈的大小也是有限的(这是一个实现问题)。

就个人而言,我使用以下准则:

  • 本地对象自动分配
  • 本地数组被延迟到std::vector<T>内部动态分配它们

它对我很有帮助(显然,这只是轶事证据)。

注意:您可以(并且可能应该)使用 RAII:智能指针或容器将动态分配对象的生命周期与堆栈变量的生命周期联系起来。

于 2012-06-02T13:46:08.187 回答
0

C++ 没有提到堆或堆栈。就语言而言,它们不存在/不是独立的事物。

至于一个实际的答案 - 使用最有效的 - 你需要快速 - 你需要保证。应用程序 A 可能会更好地处理堆上的所有内容,应用程序 B 可能会严重分割操作系统内存,从而杀死机器 - 没有正确的答案:-(

于 2012-06-02T07:23:30.520 回答
0

简而言之,除非需要,否则不要管理自己的记忆。;)

于 2012-06-02T09:16:43.737 回答
-4

Stack = 在编译时分配的静态数据。(非动态)

堆 = 在运行时分配的动态数据。(非常有活力)

尽管指针在堆栈上……这些指针很漂亮,因为它们为动态、自发地创建数据打开了大门(取决于您如何编写程序)。

(但我只是一个野蛮人,所以我说什么有什么关系)

于 2014-08-08T00:20:18.317 回答