使用堆或堆栈变量更好吗?

Som*_*bie 18 c++ variables heap stack

我前段时间和朋友讨论过.他是一位经验丰富的C++用户,我不是一位经验丰富的C++用户.他告诉我,我应该努力使用堆变量,即:

A* obj = new A("A");
Run Code Online (Sandbox Code Playgroud)

而不是:

A obj("A");
Run Code Online (Sandbox Code Playgroud)

除了使用指针很好和灵活的所有东西之外,他说最好把东西放在堆而不是堆栈中(关于堆栈的东西比堆小?).这是真的吗?如果是这样的话?

编辑:我写了一个错字,说我的朋友建议堆栈变量.他推荐堆变量.

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

Abh*_*t_K 13

根据上下文,我们需要使用堆或堆栈.每个线程都获得一个堆栈,线程通过调用函数来执行指令.调用函数时,函数变量将被推送到堆栈.当函数返回堆栈回滚并回收内存时.现在线程局部堆栈有一个大小限制,它有所不同,可以在某种程度上进行调整.如果在堆栈上创建每个对象并且对象需要大内存,则考虑到此限制,则堆栈可能会耗尽,从而导致堆栈溢出错误.除此之外,如果要通过多个线程访问对象,则将这样的对象存储在堆栈上是没有意义的.

因此,小变量,小对象和指针应存储在堆栈中.将对象存储在堆或免费存储上的问题是,内存管理变得困难.内存泄漏的可能性很大,这很糟糕.此外,如果应用程序尝试访问已删除的对象,则可能会发生访问冲突,从而导致应用程序崩溃.

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


Mes*_*sop 7

堆栈应者优先的堆,如堆栈分配的变量是自动变量:当程序进入了他们的背景下销毁是自动完成的.

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

  • 函数或代码块的局部变量{}(未由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
Run Code Online (Sandbox Code Playgroud)

在此示例中,onHeap在函数结束时仍将分配其内存.这样,如果你没有指向onHeap某个地方的指针,你将无法删除它并释放内存.这是内存泄漏,因为在程序结束之前内存将丢失.

但是,如果要返回指针onStack,因为onStack在退出函数时被销毁,使用指针可能会导致未定义的行为.虽然使用onHeap仍然完全有效.

为了更好地理解堆栈变量的工作方式,您应该搜索有关调用堆栈的信息,例如维基百科上的这篇文章.它解释了如何堆叠变量以在函数中使用.


Ale*_* C. 5

对于使用堆栈分配的变量还是堆分配的变量,没有通用的规则。只有准则,具体取决于您要执行的操作。

这是一些优点和缺点:

堆分配:

优点:

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

缺点:

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

堆栈分配:

优点:

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

缺点:

  • 灵活性较差-您必须在编译时获得所有信息(数据大小,数据结构等)
  • 较小的大小-但是,有一些方法可以计算应用程序的总堆栈大小,因此可以避免用尽堆栈

我认为这体现了一些利弊。我敢肯定还有更多。

最后,这取决于您的应用程序需求。