也许我还没有理解C ++中堆和堆栈的概念

时间:2016-06-27 11:39:52

标签: c++ memory-management

我(重新)学习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()方法返回后它们没有被销毁?

2 个答案:

答案 0 :(得分:2)

马蒂亚斯,你似乎已经足够正确地理解了“堆栈”和“堆”变量的概念。你错过的“魔法”是MyArray::Add克隆提供的s,因此它的值存在于MyArray实例中。

如果您发现“stack-”变量神秘,只需将它们视为任何范围变量。当范围超出范围时,范围变量将被破坏。函数的局部变量只是范围的特例。

要在作用域之外“生存”数据,需要在堆上创建它(使用new)并按值传递指向其位置的指针。如果要在线程之间共享数据,也是如此。另一个执行线程只是使用自己的调用堆栈执行。因此,调用相同函数的两个线程将具有其单独的本地(堆栈)变量。因此,当您漫步到多线程领域时,理解堆栈概念是很好的。

另一方面,在垃圾收集语言中,所有数据都被视为“在堆上”并通过引用计数“指针”进行访问。因此,在例如C#和Java中,您通常不会谈论“堆栈”与“堆”变量。

许多C ++类在内部实现了一些“魔术”,以便为您优化其内部存储。例如,C ++字符串可以实现短字符串优化,在本地(在堆栈上)存储“短字符串”,在堆上存储更长的字符串(使用new)。然后它应用RAII习惯用法来清除破坏时的任何堆内存。通过这种方式,您作为客户通常无需关心。就像你的例子中你不必关心TArray如何处理其内部存储器一样。您只需确保正确地将值传递给它。

再次学习C ++祝你好运:)

答案 1 :(得分:0)

MyArray.Add采用堆栈中的结构。 问题在于它是通过引用还是通过价值来实现的。

如果通过引用(指针)获取它,那么一旦它超出范围,你就会陷入混乱。

如果按值获取,则复制值(在不同范围内创建重复项)。

似乎MyArray.Add所做的是创建一个与其参数值相同的新结构。