Answers:
不,堆栈和堆之间的区别不是性能。它的生命周期:函数内的任何局部变量(任何您不使用malloc()或new的变量)都位于堆栈中。从函数返回时,它消失了。如果您希望某些东西的寿命比声明它的函数的寿命更长,则必须在堆上分配它。
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;
}
为了更清楚地了解栈是什么,请从另一端入手-而不是尝试从高级语言的角度了解栈的作用,而是查找“调用栈”和“调用约定”,然后看一下当您调用函数时,机器确实可以运行。计算机内存只是一系列地址;“堆”和“堆栈”是编译器的发明。
我会说:
如果可以的话,将其存储在堆栈中。
如果需要,将其存储在堆上。
因此,优先选择堆栈而不是堆。无法将某些内容存储在堆栈中的一些可能原因是:
使用明智的编译器,可以在堆上分配非固定大小的对象(通常是在编译时未知大小的数组)。
它比其他答案暗示的要微妙。根据声明的方式,堆栈上的数据与堆上的数据之间没有绝对的界限。例如:
std::vector<int> v(10);
在函数的主体中,该函数vector
在堆栈上声明一个由十个整数组成的(动态数组)。但是由管理的存储vector
不在堆栈中。
嗯,但是(其他答案表明)该存储的生存期vector
受其自身的生存期限制,存储的生存期是基于堆栈的,因此它的实现方式没有区别-我们只能将其视为基于堆栈的对象具有价值语义。
不是这样 假设函数是:
void GetSomeNumbers(std::vector<int> &result)
{
std::vector<int> v(10);
// fill v with numbers
result.swap(v);
}
因此swap
,在保证单个数据所有者的系统下,具有函数的任何内容(任何复杂值类型都应具有一个)可以用作对某些堆数据的可重新绑定引用。
因此,现代C ++方法永远不要将堆数据的地址存储在裸露的本地指针变量中。所有堆分配必须隐藏在类内部。
如果这样做,您可以将程序中的所有变量都视为简单的值类型,而完全不必考虑堆(除非为某些堆数据编写类似新值的包装器类,这应该是不寻常的) 。
您只需保留一些特殊的知识来帮助您优化:在可能的情况下,而不是像这样将一个变量分配给另一个变量:
a = b;
像这样交换它们:
a.swap(b);
因为它速度更快,并且不会引发异常。唯一的要求是您不必b
继续保持相同的值(a
取而代之的是获取的值,这将被丢进a = b
)。
缺点是这种方法迫使您通过输出参数而不是实际返回值从函数返回值。但是他们正在使用rvalue引用在C ++ 0x中修复该问题。
在所有最复杂的情况下,您可能会将此想法带到一个极端,并使用智能指针类(例如shared_ptr
tr1中已经存在的指针)。(尽管我认为如果您似乎需要它,则可能已经超出了标准C ++的适用范围)。
除了其他答案外,还可能涉及性能,至少一点点。除非与您无关,否则您不必担心这一点,但是:
在堆中分配需要找到一个跟踪内存块,这不是一个恒定时间的操作(并且需要一些周期和开销)。随着内存碎片化和/或您即将使用100%的地址空间,这可能会变慢。另一方面,堆栈分配是固定时间的,基本上是“免费”的操作。
要考虑的另一件事(再次,实际上只有在出现问题时才重要)是通常堆栈大小是固定的,并且可能比堆大小小得多。因此,如果要分配大对象或许多小对象,则可能要使用堆。如果堆栈空间不足,运行时将抛出站点名义异常。通常这没什么大不了的,但是要考虑另一件事。
堆栈更高效,更易于管理范围内的数据。
但是堆应该用于大于几 KB的任何东西(在C ++中很容易,只需boost::scoped_ptr
在堆栈上创建一个即可保存指向已分配内存的指针)。
考虑一个不断调用自身的递归算法。限制和猜测总堆栈使用量非常困难!而在堆上,分配器(malloc()
或new
)可以通过返回NULL
或throw
ing 指示内存不足。
资料来源:堆栈不超过8KB的Linux内核!
std::unique_ptr
,它应该比任何外部库(如Boost)都优先使用(尽管随着时间的推移它确实可以将内容提供给标准)。
为了完整起见,您可以阅读Miro Samek的文章,其中涉及在嵌入式软件的上下文中使用堆的问题。
可能已经很好地回答了。我想向您指出以下系列文章,以更深入地了解底层细节。亚历克斯·达比(Alex Darby)有一系列文章,其中将带您调试器。这是关于堆栈的第3部分。 http://www.altdevblogaday.com/2011/12/14/cc-low-level-curriculum-part-3-the-stack/