一位经验丰富的 C++ 用户告诉我,我应该努力使用堆变量,即:
A* obj = new A("A");
相对于:
A obj("A");
除了所有关于使用指针的美好和灵活的东西之外,他说最好把东西放在堆上而不是堆栈上(堆栈比堆小吗?)。这是真的吗?如果是,为什么?
注意:我知道关于寿命的问题。假设我已经适当地管理了这些变量的生命周期。(即唯一关注的标准是堆与堆栈存储,没有生命周期问题)
一位经验丰富的 C++ 用户告诉我,我应该努力使用堆变量,即:
A* obj = new A("A");
相对于:
A obj("A");
除了所有关于使用指针的美好和灵活的东西之外,他说最好把东西放在堆上而不是堆栈上(堆栈比堆小吗?)。这是真的吗?如果是,为什么?
注意:我知道关于寿命的问题。假设我已经适当地管理了这些变量的生命周期。(即唯一关注的标准是堆与堆栈存储,没有生命周期问题)
根据上下文,我们可以考虑堆或堆栈。每个线程都有一个堆栈,线程通过调用函数来执行指令。当一个函数被调用时,函数变量被压入堆栈。并且当函数返回堆栈回滚并回收内存时。现在线程本地堆栈有一个大小限制,它会有所不同并且可以在一定程度上进行调整。考虑到这一点,如果每个对象都是在堆栈上创建的,并且该对象需要大量内存,那么堆栈空间将耗尽,从而导致 stackoverflow 错误。除此之外,如果要由多个线程访问该对象,则将此类对象存储在堆栈上是没有意义的。
因此,小变量、小对象的大小可以在编译时确定,指针应该存储在堆栈上。将对象存储在堆或自由存储上的问题是,内存管理变得困难。有内存泄漏的机会,这很糟糕。此外,如果应用程序尝试访问已被删除的对象,则可能会发生访问冲突,从而导致应用程序崩溃。
C++11 引入了智能指针(共享、唯一),使堆内存管理更容易。实际引用的对象在堆上,但被始终在堆栈上的智能指针封装。因此,当在函数返回事件或异常期间堆栈回滚时,智能指针的析构函数会删除堆上的实际对象。在共享指针的情况下,引用计数保持不变,当引用计数为零时,实际对象被删除。 http://en.wikipedia.org/wiki/Smart_pointer
没有关于使用堆栈分配变量和堆分配变量的一般规则。只有指导方针,这取决于您要做什么。
以下是一些优点和缺点:
堆分配:
优点:
缺点:
堆栈分配:
优点:
缺点:
我认为这抓住了一些优点和缺点。我敢肯定还有更多。
最后,这取决于您的应用程序需要什么。
堆栈应该优先于堆,因为堆栈分配的变量是自动变量:当程序脱离其上下文时,它们的销毁会自动完成。
事实上,在栈和堆上创建的对象的生命周期是不同的:
{}
(不是由 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 上的这篇文章。它解释了如何堆叠变量以在函数中使用。
在 C++ 中尽可能避免使用new总是更好的。
但是,有时您无法避免它。
例如:
希望变量存在于其范围之外。
所以它应该是真正的课程,但如果你有选择,总是避免堆分配变量。
答案并不像某些人让你相信的那样明确。
通常,您应该更喜欢自动变量(在堆栈上),因为它更简单。然而,有些情况需要动态分配(在堆上):
后者有点棘手。理论上,自动变量可以无限分配,但计算机是有限的,更糟糕的是,大多数时候堆栈的大小也是有限的(这是一个实现问题)。
就个人而言,我使用以下准则:
std::vector<T>
内部动态分配它们它对我很有帮助(显然,这只是轶事证据)。
注意:您可以(并且可能应该)使用 RAII:智能指针或容器将动态分配对象的生命周期与堆栈变量的生命周期联系起来。
C++ 没有提到堆或堆栈。就语言而言,它们不存在/不是独立的事物。
至于一个实际的答案 - 使用最有效的 - 你需要快速 - 你需要保证。应用程序 A 可能会更好地处理堆上的所有内容,应用程序 B 可能会严重分割操作系统内存,从而杀死机器 - 没有正确的答案:-(
简而言之,除非需要,否则不要管理自己的记忆。;)
Stack = 在编译时分配的静态数据。(非动态)
堆 = 在运行时分配的动态数据。(非常有活力)
尽管指针在堆栈上……这些指针很漂亮,因为它们为动态、自发地创建数据打开了大门(取决于您如何编写程序)。
(但我只是一个野蛮人,所以我说什么有什么关系)