-1

I am (re-) learning C++ (again, after many years of Java and Python), but it seems I am not familiar with the concepts of heap and stack any more. I am reading threads like this one, which makes it quite clear: local variables in functions/methods live on the stack and will be destroyed when leaving that function/method. For objects that I need longer I would need to allocate memory on the heap.

Fine. But not I am reading a bit of C++ code and see the following (simplified):

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);
    }
}

So there is a custom struct MyStruct, and a class that has a container MyArray. Then at some point I call MyClass->FillArray() to initialize the array. In that method I create objects of MyStruct on the stack (i.e. not via new) and add them to the array. From my understanding these objects within the array should be destroyed as soon as the FillArray() methods returns.

Now at some point later in code I call MyClass->GetArray(). And to my surprise the array returned does indeed contain all the struct objects that have been created before.

Why are these struct objects still there, why have they not been destroyed as soon as the FillArray() method returns?

Community
  • 1
  • 1
Matthias
  • 9,817
  • 14
  • 66
  • 125
  • 3
    You need to find out how `Add` works. Most likely it copies `s` into the `MyArray` like `push_back` does for `std::vector`. – NathanOliver Jun 27 '16 at 11:42
  • `TArray` is what this is all about, yet you completely ignored it. – LogicStuff Jun 27 '16 at 11:42
  • 2
    There is no heap or stack (except for the container class with identical name) in the entire C++ language standard. You are trying to learn something that is not contractual. – IInspectable Jun 27 '16 at 11:43
  • @IInspectable I don't understand you. Isn't there a difference in C++ when I allocate memory via `new` vs. creating a local variable in a function (as discussed in the thread I have linked to)? – Matthias Jun 27 '16 at 12:07
  • Why is my question being downvoted? I am describing my what I am looking for, giving code examples, explaining what I tried so far (or better: what I thought I would understand), and asking a specific question in the end. Really surprised of all these downvotes :( – Matthias Jun 27 '16 at 12:10
  • 1
    Yes, there is a difference between objects with automatic storage duration and dynamic storage duration. The difference is not, that one is (supposedly) stored on something called *stack* or *heap*. The difference is the storage duration (and related lifetime). – IInspectable Jun 27 '16 at 12:14
  • @IInspectable Ok, I see.But when I read online about this topic, nearly every page and article refers to *heap* and *stack* in that context. So does it not make sense if I refer to those terms in my question as well? – Matthias Jun 27 '16 at 12:20
  • 1
    If certain terms are meaningless in the context of a programming language, then it doesn't make sense to use those terms to refer to anything. The votes on your question seem to be trying to get this across. Unless the resources you are learning from contain a disclaimer of the form *"a typical implementation might do this or that"*, you should stop learning from those. They are confusing contractual guarantees with implementation details. And you seem to have followed that route. – IInspectable Jun 27 '16 at 12:25

2 Answers2

2

Matthias, you seem to have grasped the concept of "stack" and "heap" variables correctly enough. The "magic" you missed is that MyArray::Add clones the provided s and thus its value lives on in the MyArray instance.

If you find "stack-" variables mysterious just think of them as any scoped variable. A scoped variable is destructed when it goes out of scope. And a local variable of a function is just a special case of a scope.

To have data "live on" outside a scope you need to create it on the heap (use new) and pass on the pointer to its location by value. This is also true if you want to share data between threads. Another thread-of-execution is just execution using a call-stack of its own. And thus two threads calling the same function will have their separate local (stack) variables. So understanding the stack-concept is good when you wander into the land of multi-threading.

In garbage collected languages, on the other hand, all data is treated as if "on the heap" and accessed thorough reference counted "pointers". Thus in e.g., C# and Java you don not normally talk about "stack-" versus "heap-" variables.

Many C++ classes implements some "magic" internally to optimise its internal storage for you. C++ strings for example may implement short string optimisation in that stores "short strings" locally (on the stack) and longer strings on the heap (using new). It then applies the RAII idiom to clean up any heap-memory on destruction. In this way you as client does normally not have to care. Just as in your example where you do not have to care how TArray handles its internal memory. You just make sure you pass the values to it correctly.

Good luck with learning C++ again :)

Matthias
  • 9,817
  • 14
  • 66
  • 125
0

MyArray.Add takes a struct that's in the stack. The question is wether it takes it by reference or by value.

If it takes it by reference (pointer), then once it goes out of scope you'll end up in a mess.

If it takes it by value, it copies the values (creating duplicates in a different scope).

It seems like what MyArray.Add does is create a new struct with the same values as it's parameter.

Ivan Rubinson
  • 3,001
  • 4
  • 19
  • 48
  • It actually does not matter wether it takes the argument by reference or by value as long as it stores it as copy of the passed value (does not store a pointer or reference to it). Before "moving" was introduced to C++ it was common practice to almost always pass by reference to function calls (to avoid the extra copying unless it was needed). In modern C++ a common advice is to pass by value and let the compiler copy-elide if possible. but now ve are quite off-topic here :) – Kjell-Olov Högdahl Jun 29 '16 at 12:12