我(重新)学习C ++(再次,经过多年的Java和Python),但似乎我不再熟悉堆和堆栈的概念了。我正在阅读threads like this one,这很清楚:函数/方法中的局部变量存在于堆栈中,并且在离开该函数/方法时将被销毁。对于我需要更长时间的对象,我需要在堆上分配内存。
精细。但不是我正在阅读一些C ++代码并看到以下(简化):
struct MyStruct
{
int Integer;
bool Boolean;
}
// in header file
class MyClass
{
TArray<MyStruct> MyArray; // TArray is a custom dynamic array in the framework
void FillArray();
TArray<MyStruct> GetArray() { return MyArray; }
}
// in cpp file
void MyClass::FillArray()
{
for(int i = 0; i < 10; ++i)
{
MyStruct s; // These objects are created on the stack, right?
s.Integer = i;
s.Boolean = true;
MyArray.Add(s);
}
}
因此,有一个自定义结构MyStruct
,以及一个具有容器MyArray
的类。然后在某些时候我调用MyClass->FillArray()
来初始化数组。在该方法中,我在堆栈上创建MyStruct
的对象(即不通过new
)并将它们添加到数组中。根据我的理解,一旦FillArray()
方法返回,就应该销毁数组中的这些对象。
现在稍后我会在代码中调用MyClass->GetArray()
。令我惊讶的是,返回的数组确实包含之前创建的所有结构对象。
为什么这些struct对象仍然存在,为什么在FillArray()
方法返回后它们没有被销毁?
答案 0 :(得分:2)
MyArray::Add
克隆提供的s,因此它的值存在于MyArray实例中。
如果您发现“stack-”变量神秘,只需将它们视为任何范围变量。当范围超出范围时,范围变量将被破坏。函数的局部变量只是范围的特例。
要在作用域之外“生存”数据,需要在堆上创建它(使用new
)并按值传递指向其位置的指针。如果要在线程之间共享数据,也是如此。另一个执行线程只是使用自己的调用堆栈执行。因此,调用相同函数的两个线程将具有其单独的本地(堆栈)变量。因此,当您漫步到多线程领域时,理解堆栈概念是很好的。
另一方面,在垃圾收集语言中,所有数据都被视为“在堆上”并通过引用计数“指针”进行访问。因此,在例如C#和Java中,您通常不会谈论“堆栈”与“堆”变量。
许多C ++类在内部实现了一些“魔术”,以便为您优化其内部存储。例如,C ++字符串可以实现短字符串优化,在本地(在堆栈上)存储“短字符串”,在堆上存储更长的字符串(使用new
)。然后它应用RAII习惯用法来清除破坏时的任何堆内存。通过这种方式,您作为客户通常无需关心。就像你的例子中你不必关心TArray如何处理其内部存储器一样。您只需确保正确地将值传递给它。
再次学习C ++祝你好运:)
答案 1 :(得分:0)
MyArray.Add采用堆栈中的结构。 问题在于它是通过引用还是通过价值来实现的。
如果通过引用(指针)获取它,那么一旦它超出范围,你就会陷入混乱。
如果按值获取,则复制值(在不同范围内创建重复项)。
似乎MyArray.Add所做的是创建一个与其参数值相同的新结构。