C++ 堆栈与堆分配
Posted
技术标签:
【中文标题】C++ 堆栈与堆分配【英文标题】:C++ stack vs heap allocation 【发布时间】:2016-04-15 11:56:14 【问题描述】:我想知道什么时候应该在 C++ 的堆栈上分配一个类?我在 Java 方面有很强的背景,在 Java 中,所有类都使用 new
关键字在堆上分配。在 C++ 中,我可以在堆栈分配和堆分配之间进行选择,但现在引入了智能指针,使用 std::unique_ptr
分配不转移所有权的所有内容更有意义。
我真的想不出在哪些情况下有必要或更好地使用堆栈分配。也许是为了在嵌入式系统中进行某种优化?
【问题讨论】:
仅供参考,std::unique_ptr
是一个基于“堆栈”的对象。顺便说一句,我们称之为自动和动态对象,因为 C++ 没有堆栈或堆的概念。恕我直言,您希望尽可能使用自动对象,
您通常使用相反的原则:如果不需要动态分配,您就不要这样做。动态分配默认为“过早悲观化”。
【参考方案1】:
只要函数范围(或函数内部的for
、while
、if
等控制块的范围)与对象的生命周期相匹配,就使用自动(堆栈)分配需要。这样,如果对象拥有/控制任何资源,例如动态分配的内存、文件句柄等 - 它们将在析构函数调用期间被释放,因为该范围已离开。 (不会在垃圾收集器结束时的某个不可预测的时间)。
仅在有明确需要时才使用new
,例如:
需要对象的寿命比函数范围长,
将所有权移交给其他代码
拥有一个指向基类的指针容器,然后您可以对其进行多态处理(即使用虚拟分派到派生类函数实现),或者
一个特别大的分配会占用大部分堆栈(您的操作系统/进程将“协商”一个限制,通常在 1-8+ 兆字节范围内)
如果这是您使用动态分配的唯一原因,并且您确实希望对象的生命周期与函数中的范围相关联,则应使用本地std::unique_ptr<>
来管理动态内存,并确保它无论您如何离开范围都会释放:return
、throw
、break
等。(您也可以在 class
/struct
中使用 std::unique_ptr<>
数据成员来管理任何内存对象拥有。)
Mathieu Van Nevel 在下面讨论 C++11 移动语义 - 相关性是,如果您在堆栈上有一个控制大量动态分配(堆)内存的小型管理对象,移动语义会给予额外的保证和罚款- 管理对象何时将其资源移交给其他代码拥有的另一个管理对象(通常是调用者,但可能是一些其他容器/对象寄存器)的粒度控制。这种切换可以避免堆上的数据被复制/复制,即使是暂时的。此外,elision 和 return-value-optimisation 通常允许名义上自动/堆栈托管的变量直接在它们最终被分配/返回的内存中构造,而不是以后复制到那里。
【讨论】:
这是一个很好的总结。我最近重新编写了一些代码,将一组非常大的对象放入堆栈。如果您不小心,可能会在较大的程序或深度递归的程序中耗尽堆栈空间。 关于 c++11 移动语义的一些细节,我认为答案将是完美的:upvote 好总结,如果你补充一下我什么时候应该使用 std::unique_ptr 而不是堆栈分配,我会给你答案。 不想听起来粗鲁,我就是这样。【参考方案2】:在大型代码库中,我们对简单的结构对象使用堆栈分配,而对于更复杂的类(涉及多态性、内存所有权等),我们总是使用动态分配。
【讨论】:
以上是关于C++ 堆栈与堆分配的主要内容,如果未能解决你的问题,请参考以下文章