C++堆栈分配与堆分配

11

我在想,什么时候应该在C++中在堆栈上分配类? 我在Java方面有很强的背景,在Java中,所有类都在堆上分配,使用new关键字。 在C ++中,我可以选择在堆栈和堆上进行分配,但是现在智能指针已经引入,使用std::unique_ptr分配不传递所有权的所有内容更合理。

我实在想不出任何情况下需要或更好地使用堆栈分配。也许在嵌入式系统中进行某种优化?


2
只是提醒一下,std::unique_ptr 是一个基于“栈”(stack)的对象。顺便说一下,我们称它们为自动对象和动态对象,因为 C++ 没有堆栈概念。在我看来,尽可能使用自动对象是最好的选择。 - NathanOliver
10
通常情况下,你应该采用相反的原则:如果不需要动态分配,就不要使用它。默认情况下进行动态分配是“过早优化”。请注意,本次翻译已经尽力使内容更加易懂通俗,但没有改变原意。 - molbdnilo
2个回答

20
每当函数范围或函数内的控制块(如 for, while, if等)的范围与对象需要的生命周期相符时,使用自动(堆栈)分配。这样,如果对象拥有/控制任何资源,例如动态分配的内存、文件句柄等,它们将在析构函数调用时释放,因为该范围已被离开。(不是在垃圾收集器以后的某个不可预测的时间)。仅在存在明显需要时才使用 new,例如:
  • 需要对象的生存期长于函数范围,

  • 将所有权移交给其他代码,

  • 具有指向基类的指针容器,然后可以对其进行多态处理(即使用虚派生类函数实现),或

  • 特别大的分配将会占用大量的堆栈空间(您的操作系统/进程通常会谈判达成限制,通常在1-8+兆字节的范围内)。

    • 如果这是您使用动态分配的唯一原因,并且确实希望对象的生命周期与函数中的范围相关联,则应使用本地 std::unique_ptr<> 来管理动态内存,并确保无论如何离开该范围,都将释放它:通过 return, throw, break等。(您也可以在 class/struct 中使用 std::unique_ptr<> 数据成员来管理对象拥有的任何内存。)
Mathieu Van Nevel 在下面评论了 C++11 移动语义 - 相关性在于,如果您在堆栈上有一个小巧的管理对象,它控制着大量的动态分配(堆)内存,移动语义可以提供额外的保证和细粒度的控制,以便管理对象何时将其资源移交给其他代码拥有的另一个管理对象(通常是调用者,但可能是某个其他容器/对象注册表)。此切换可以避免堆上的数据被复制/重复,即使瞬间也是如此。此外,省略和返回值优化通常允许名义上的自动/堆栈托管变量直接在它们最终被分配/返回的某些内存中构造,而不是稍后复制到那里。

这是一个很好的总结。最近我重构了一些代码,该代码将一个非常大的对象数组放在堆栈中。如果不小心处理,您可能会在较大的程序或具有深度递归的程序中耗尽堆栈空间。 - Kyle A
关于C++11移动语义的一些细节,如果回答完美的话,我想会得到点赞。 - Mathieu Van Nevel
很好的总结,如果你加上什么时候应该使用std::unique_ptr而不是堆栈分配,我会给你答案的勾选。 - AteszDude
不想听起来粗鲁,我只是一直这样。 - AteszDude

1
在大型代码库中,对于简单结构对象,我们使用堆栈分配,而对于更复杂的类(涉及多态性、内存所有权等),我们总是使用动态分配。

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