C ++中正确的堆栈和堆使用情况?
我已经编程了一段时间,但主要是Java和C#。 我从来没有必要自己管理记忆。 我最近开始使用C ++进行编程,我对于何时应该将东西存储在堆栈上以及何时将其存储在堆上有些困惑。
我的理解是,非常频繁访问的变量应该存储在堆栈中,并且对象,很少使用的变量以及大型数据结构都应该存储在堆中。 这是正确的还是我不正确?
不,堆栈和堆之间的区别不是性能。 它的使用寿命:函数内部的任何局部变量(任何你不需要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
功能的swap
函数(以及任何复杂的值类型都应该具有swap
函数)可用作某种堆数据的一种可重新引用的引用,该系统保证该数据的单个所有者。
因此,现代C ++方法是永远不会将堆数据的地址存储在裸指针变量中。 所有的堆分配必须隐藏在类中。
如果你这样做了,你可以将程序中的所有变量看作是简单的值类型,并且完全忘记堆(除非为一些堆数据编写新的值类封装类,这应该是不寻常的) 。
你只需保留一点特殊的知识来帮助你优化:在可能的情况下,而不是像下面这样将一个变量赋值给另一个变量:
a = b;
把它们换成这样:
a.swap(b);
因为它速度更快,并且不会抛出异常。 唯一的要求是你不需要b
继续保持相同的值(它会得到a
值,而这会在a = b
被删除)。
缺点是这种方法迫使你通过输出参数而不是实际的返回值从函数返回值。 但他们正在用rvalue引用修正C ++ 0x。
在所有最复杂的情况下,你会把这个想法带到一般的极端,并使用一个智能指针类,例如已经在tr1中的shared_ptr
。 (虽然我认为如果你似乎需要它,你可能已经超出了标准C ++的适用范围。)