C ++中正确的堆栈和堆使用情况?

我已经编程了一段时间,但主要是Java和C#。 我从来没有必要自己pipe理记忆。 我最近开始使用C ++进行编程,对于什么时候应该将东西存储在堆栈以及何时将其存储在堆中,我有点困惑。

我的理解是,被频繁访问的variables应该被保存在堆栈中,对象,很less使用的variables和大型的数据结构都应该被保存在堆中。 这是正确的还是我不正确?

不,堆栈和堆的区别不是性能。 它的使用寿命:函数内的任何局部variables(任何你不是malloc()或新的)在栈上。 从函数返回时它会消失。 如果你想要的东西比宣布它的函数寿命更长,你必须在堆上分配它。

class Thingy; Thingy* foo( ) { int a; // this int lives on the stack Thingy B; // this thingy lives on the stack and will be deleted when we return from foo Thingy *pointerToB = &B; // this points to an address on the stack Thingy *pointerToC = new Thingy(); // this makes a Thingy on the heap. // pointerToC contains its address. // this is safe: C lives on the heap and outlives foo(). // Whoever you pass this to must remember to delete it! return pointerToC; // this is NOT SAFE: B lives on the stack and will be deleted when foo() returns. // whoever uses this returned pointer will probably cause a crash! return pointerToB; } 

为了更清楚地了解堆栈是什么,请从另一端进行讨论 – 而不是尝试从高级语言的angular度理解堆栈的作用,查看“调用堆栈”和“调用约定”,然后查看当你调用一个函数时,机器确实会这样做。 计算机内存只是一系列的地址; “堆”和“堆栈”是编译器的发明。

我会说:

如果可以的话,将其存储在堆栈中。

将它存储在堆上,如果你需要的话。

因此,更喜欢堆栈堆。 一些可能的原因,你不能在堆栈上存储的东西是:

  • 这太大了 – 在32位操作系统上的multithreading程序中,堆栈有一个小的固定的(至less创build线程的时间)大小(通常只有几个兆),这样就可以创build大量的线程而不用耗尽地址对于64位程序或者单线程(反正是Linux)的程序来说,这并不是什么大问题,在32位的Linux下,单线程程序通常使用dynamic堆栈,它们可以一直增长到堆栈顶端。
  • 您需要在原始堆栈框架的范围之外访问它 – 这确实是主要原因。

使用合理的编译器,可以在堆上分配非固定大小的对象(通常是在编译时不知道大小的数组)。

这比其他答案的build议更微妙。 根据声明的方式,堆栈上的数据和堆上的数据之间没有绝对的分歧。 例如:

 std::vector<int> v(10); 

在函数的主体中,它声明了堆栈上的十个整数的vector (dynamic数组)。 但由vectorpipe理的存储不在堆栈上。

嗯,但是(其他的回答表明)存储的生命周期是以vector本身的生命周期为基础的,这里基于堆栈,所以它的实现方式没有什么区别 – 我们只能把它看作是基于堆栈的具有价值语义的对象。

不是这样。 假设这个函数是:

 void GetSomeNumbers(std::vector<int> &result) { std::vector<int> v(10); // fill v with numbers result.swap(v); } 

所以任何具有swapfunction(以及任何复杂的值types都应该有的function)的任何东西都可以作为一种对一些堆数据的可重新引用的参考,在保证该数据的单个所有者的系统下。

因此,现代的C ++方法是永远不会将堆数据的地址存储在裸指针variables中。 所有的堆分配必须隐藏在类中。

如果你这样做的话,你可以把程序中的所有variables看作是简单的值types,并且完全忘记堆(除非为一些堆数据编写一个新的值类包装类,这应该是不寻常的) 。

你只需要保留一个特殊的知识点来帮助你优化:在可能的情况下,而不是像下面这样将一个variables赋值给另一个variables:

 a = b; 

把它们换成这样:

 a.swap(b); 

因为速度更快,并且不会抛出exception。 唯一的要求是,你不需要b继续保持相同的值(它会得到a值,而这将被抛弃在a = b )。

缺点是这种方法迫使你通过输出参数而不是实际的返回值从函数返回值。 但是他们正在用右值引用修正C ++ 0x。

在所有最复杂的情​​况下,你会把这个想法带到一般的极端,并使用一个智能指针类,例如已经在tr1中的shared_ptr 。 (虽然我认为如果你似乎需要它,你可能已经超出了标准C ++的适用范围。

如果需要在创build函数的作用域之外使用,则还可以将其存储在堆上。 一个与堆栈对象一起使用的习惯称为RAII – 这涉及到使用基于堆栈的对象作为资源的包装,当对象被销毁时,资源将被清理。 基于堆栈的对象更易于跟踪何时抛出exception – 您不需要关心在exception处理程序中删除基于堆的对象。 这就是为什么在现代C ++中通常不使用原始指针的原因,您可以使用智能指针,该指针可以是基于堆栈的包装器,用于指向基于堆的对象的原始指针。

为了增加其他答案,它也可以是性能,至less有一点点。 不是你应该担心,除非它与你有关,但是:

在堆中分配需要find跟踪一块内存,这不是一个恒定的操作(并需要一些周期和开销)。 随着内存变得分散,这可能会变得更慢,并且/或者您已经接近使用100%的地址空间。 另一方面,堆栈分配是恒定的,基本上是“空闲”操作。

另一个要考虑的事情(再次,真的只有当它成为一个问题才是重要的)是通常堆栈大小是固定的,并且可以比堆大小低得多。 所以如果你要分配大对象或许多小对象,你可能想要使用堆; 如果用完堆栈空间,则运行时将抛出该网站的标题exception。 通常不是什么大事,但要考虑另一件事情。

堆栈效率更高,更容易pipe理范围数据。

但堆应该用于大于 KB的任何东西 (在C ++中很容易,只需在堆栈上创build一个boost::scoped_ptr来存放指向已分配内存的指针)。

考虑一个不断调用自身的recursionalgorithm。 这是很难限制和猜测总的堆栈使用情况! 而在堆上,分配器( malloc()new )可以通过返回NULLthrow来指示内存不足。

来源 :Linux内核的堆栈不大于8KB!

为了完整起见,您可以阅读Miro Samek关于在embedded式软件环境中使用堆的问题的文章。

一堆问题

根据variables的分配方式select是在堆上还是在堆栈上分配。 如果你dynamic地分配一些东西,使用“新的”调用,你正在从堆中分配。 如果将某些东西分配为全局variables,或者将其作为函数中的参数分配到堆栈中。

我认为有两个决定因素

 1) Scope of variable 2) Performance. 

在大多数情况下,我倾向于使用堆栈,但是如果您需要访问variables以外的范围,则可以使用堆。

为了在使用堆时提高性能,您还可以使用该function来创build堆块,这可以帮助获得性能,而不是将每个variables分配到不同的内存位置。

大概这个答案已经很好了。 我想指出下面一系列的文章,对低级细节有更深的理解。 Alex Darby有一系列的文章,他用debugging器引导你。 这是关于堆栈的第3部分。 http://www.altdevblogaday.com/2011/12/14/cc-low-level-curriculum-part-3-the-stack/