When the application enters a scoped environment (block, function call, etc.), the runtime loads the context (including local variables) for that block onto the stack. This is an actual stack data structure. As execution goes deeper and deeper into nested contexts, the stack gets higher and higher. If main() calls foo() which calls bar(), the stack will have main()s context on the bottom of the stack, then foo's context, then bar's. This is why infinite recursion results in "stack overflow" and throwing exceptions triggers "stack unwinding."
When execution exits that scope, that context is popped off the stack. In C++, popping objects of the stack includes invoking the destructor for those objects. So when bar() returns, its local variables will be popped off the stack, and the destructors for those variables will be invoked.
You're probably misunderstanding the characteristics of a few programming languages. C++ does not have a garbage collector, so it does not decide when an object has gone out of scope: the user does.
int main()
{
Obj * obj = new Obj;
Obj obj2;
delete obj;
}
In the function above, you create an object obj in the heap, and release it when you're no longer going to use it. The object obj2, on the other hand, just terminates its life at the end of main(), as any other variable. When an object terminates its life, the destructor of the object is called automatically; the compiler inserts the calls to these destructors automatically: at the end of the function, or when operator delete is invoked.