2

People always talk about how objects created without the new keyword are destroyed when they go out of scope, but when I think about this, it seems like that's wrong. Perhaps the destructor is called when the variable goes out of scope, but how do we know that it is no longer taking up space in the stack? For example, consider the following:

void DoSomething()
{
  {
    My_Object obj;
    obj.DoSomethingElse();
  }
  AnotherFuncCall();
}

Is it guaranteed that obj will not be saved on the stack when AnotherFuncCall is executed? Because people are always saying it, there must be some truth to what they say, so I assume that the destructor must be called when obj goes out of scope, before AnotherFuncCall. Is that a fair assumption?

Daniel
  • 6,595
  • 9
  • 38
  • 70

9 Answers9

4

You are confusing two different concepts.

Yes, your object's destructor will be called when it leaves its enclosing scope. This is guaranteed by the standard.

No, there is no guarantee that an implementation of the language uses a stack to implement automatic storage (i.e., what you refer to as "stack allocated objects".)

Since most compilers use a fixed size stack I'm not even sure what your question is. It is typically implemented as a fixed size memory region where a pointer move is all that is required to "clean up" the stack as that memory will be used again soon enough.

So, since the memory region used to implement a stack is fixed in size there is no need to set the memory your object took to 0 or something else. It can live there until it is needed again, no harm done.

Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • Right, I just was thinking about a scenario where you could run out of stack space with all of these objects that will never be used again. I guess it would be impractical to shift all of the objects above it in the stack, but it would be an interesting optimization if the stack pointer could be moved down to get rid of objects on the top of the stack that will never be used again. – Daniel Jun 22 '12 at 20:07
  • I didn't mean deallocating the memory. – Daniel Jun 22 '12 at 20:07
  • @Daniel: If the stack pointer is at the end of the stack then it needs to be due to the number of functions called and the size of their variables. The stack pointer is moved back when a function returns, which is why recursive functions (in C++) are an easy way to overrun your stack because the call count keeps on increasing. – Ed S. Jun 22 '12 at 20:09
  • I don't think that's true. The base pointer keeps track of that, right? – Daniel Jun 22 '12 at 20:11
  • @Daniel: I'm not sure what you mean exactly. [Here's a good explanation of stack v base pointer](http://stackoverflow.com/questions/1395591/what-is-exactly-the-base-pointer-and-stack-pointer-to-what-do-they-point). – Ed S. Jun 22 '12 at 20:15
  • Right, I understand that. So why does the stack pointer have to be in a particular place? When the function returns, doesn't the stack pointer get set to whatever is currently in the base pointer and then the base pointer is popped off of the stack? – Daniel Jun 22 '12 at 20:17
1

I believe it depends where in the stack the object was created. If it was on the bottom (assuming stack grows down) then I think the second function may overwrite the destroyed objects space. If the object was inside the stack, then probably that space is wasted, since all further objects would have to be shifted.

emesx
  • 12,555
  • 10
  • 58
  • 91
1

Your stack is not dynamically allocated and deallocated, it's just there. Your objects constructors and destructors will get called but you don't get the memory back.

Rocky Pulley
  • 22,531
  • 20
  • 68
  • 106
  • I don't mean deallocating the memory. I mean could it essentially be removed from the stack. Thinking back on it, that would be difficult if the object is not on the top of the stack. – Daniel Jun 22 '12 at 20:08
  • things don't get "removed from the stack". – Rocky Pulley Jun 22 '12 at 20:08
  • They are "removed" when the stack pointer is set back to the base pointer. I'm talking about removing one element from the stack by decrementing (or incrementing if the stack grows down) the stack pointer by `sizeof(obj)` – Daniel Jun 22 '12 at 20:11
  • @Daniel - Adjusting the stack pointer several times inside a function adds a runtime cost (more instructions), so that is usually not done. However, the compiler is free to use the same space for other objects later in the function. – Bo Persson Jun 23 '12 at 01:36
1

Because people are always saying it, there must be some truth to what they say, so I assume that the destructor must be called when obj goes out of scope, before AnotherFuncCall. Is that a fair assumption?

This is correct. Note that this final question says nothing about a stack". Whether an implementation uses a stack, or something else, is up to the implementation.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
1

What people say is indeed true. The object still remains in the memory location. However, the way stack works means that the object does not take any memory space from stack.

What usually happens when memory is allocated on the stack is that the stack pointer is decremented by sizeof(type) and when the variable goes out of scope and the object is freed, the stack pointer is incremented, thus shrinking the effective size of data allocated on the stack. Indeed, the data still resides in it's original address, it is not destroyed or deleted at all.

And just to clarify, the C++ standard says absolutely nothing about this! The C++ standard is completely unaware of anything called stack or heap in sense of memory allocation because they are platform specific implementation details.

zxcdw
  • 1,629
  • 1
  • 10
  • 18
1

Objects created "on the stack" in local scope have what is called automatic storage duration. The Standard says:

C++03 3.7.2 Automatic storage duration

1/ Local objects explicitly declared auto or register or not explicitly declared static or extern have automatic storage duration. The storage for these objects lasts until the block in which they are created exits.

2/ [Note: these objects are initialized and destroyed as described in 6.7. ]

On the destruction of these objects:

6.7 Declaration statement

2/ Variables with automatic storage duration (3.7.2) are initialized each time their declaration-statement is executed. Variables with automatic storage duration declared in the block are destroyed on exit from the block (6.6).

Hence, according to the Standard, when object with local scope fall out of scope, the destructor is called and the storage is released.

Weather or not that storage is on a stack the Standard doesn't say. It just says the storage is released, wherever it might be.

Some architectures don't have stacks in the same sense a PC has. C++ is meant to work on any kind of programmable device. That's why it never mentions anything about stacks, heaps, etc.

On a typical PC-type platform running Windows and user-mode code, these automatic variables are stored on a stack. These stacks are fixed-size, and are created when the thread starts. As they become instantiated, they take up more of the space on the stack, and the stack pointer moves. If you allocate enough of these variables, you will overflow the stack and your program will die an ugly death.

Try running this on a Windows PC and see what happens for an example:

int main()
{
    int boom[10000000];
    for( int* it = &boom[0]; it != &boom[sizeof(boom)/sizeof(boom[0])]; ++it )
        *it = 42;
}
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • Fsck the standard in this matter, it doesn't tell anything about the stack, what it is concern with is object life-time, initialization and destruction. There could be no stack after all :) –  Jun 22 '12 at 20:16
0

Your local variables on stack do not take extra memory. The system provides some memory from each thread's stack, and the variables on the stack just use part of it. After running out of the scope, the compiler can reuse the same part of the stack for other variables (used later in the same function).

mity
  • 2,299
  • 17
  • 20
  • What if the 'system' doesn't support threads at all?? I has nothing to do with threads at all! – πάντα ῥεῖ Jun 22 '12 at 20:10
  • Then your process still has one thread (the main and only one) and my argument is still valid. – mity Jun 22 '12 at 20:10
  • @mity: You can use C++ in environments that don't even have the concept of a 'thread' (allthough you might call the complete execution path a 'thread'), think about e.g. co-routine concepts ... – πάντα ῥεῖ Jun 22 '12 at 20:16
  • @JohnDibling: Define "take". Stack has a fixed-size and is allocated before the process is put into running state. Then you can look at it as taking memory, or not... you just use that stack by moving the stack frame address "up" and "down". –  Jun 22 '12 at 20:18
  • @VladLazarenko: Regardless, as you allocate automatic variables on a typical system that uses stacks, less memory is available to allocate additional automatic storage duration objects. – John Dibling Jun 22 '12 at 20:22
  • @John Dibling: yes but indirectly. They eat part of stack, but the stack is there even if you remove the variable from your program. – mity Jun 22 '12 at 20:22
0
how do we know that it is no longer taking up space in the stack?

We don't. There are way to see whether they do or don't, but those are architecture and ABI specific. Generally, functions do pop whatever they pushed to the stack when they return control to the caller. What C/C++ guarantees is that it will call a destructor of high-level objects when they leave the scope (though some older C++ like MSVC 6 had terrible bugs at a time when they did not).

Is it guaranteed that obj will not be saved on the stack when AnotherFuncCall is executed? 

No. It is up to the compiler to decide when and how to push and pop stack frames as long as that way complies with ABI requirements.

0

The question "Is something taking up space in the stack" is a bit of a loaded question, because in reality, there is no such thing as free space (at a hardware level.) A lot of people (myself included, at one point) thought that space on a computer is freed by actually clearing it, i.e. changing the data to zeroes. However, this is actually not the case, as doing so would be a lot of extra work. It takes less time to do nothing to memory than it does to clear it. So if you don't need to clear it, don't! This is true for the stack as well as files you delete from your computer. (Ever noticed that "emptying the recycle bin" takes less time than copying those same files to another folder? That's why - they're not actually deleted, the computer just forgets where they're stored.)

Generally speaking, most hardware stacks are implemented with a stack pointer, which tells the CPU where the next empty slot in the stack is. (Or the most recent item pushed on the stack, again, this depends on the CPU architecture.)

When you enter a function, the assembly code subtracts from the stack pointer to create enough room for your local variables, etc. Once the function ends, and you exit scope, the stack pointer is increased by the same amount it was originally decreased, before returning. This increasing of the stack pointer is what is meant by "the local variables on the stack have been freed." It's less that they've been freed and more like "the CPU is now willing to overwrite them with whatever it wants to without a second thought."

Now you may be asking, if our local variables from a previous scope still exist, why can't we use them? Reason being, there's no guarantee they'll still be there from the time you exit scope and the time you try to read them again.

puppydrum64
  • 1,598
  • 2
  • 15