I don't understand when and how e is out of scope
The scope of e
is everything from its declaration to the end of its enclosing block scope:
void func()
{ // start of block scope
Entity e; // e created in the enclosing block scope here
e.Print();
} // end of block scope, e (and any other automatic locals) destroyed
but all that only gets executed when I call func in main right?
Now you're mixing up two things:
- scope is the section of the source code where
e
is a valid identifier (and refers to the same object, in case you have other variables with the same name in other code). This is static, it's all known at compile time.
- control flow is what when
func
is actually executed, enters its block scope, executes the statements in the function body, and finally exits block scope (destroying any automatic local objects at the same time). This happens at runtime, and is dynamic (in the sense that it might depend on conditional logic, or input).
So only when I call func in main e gets created and the method gets called but how exactly is it ever out of scope?
It has automatic scope, so its scope ends when the enclosing block does. For e
, that's when func
has finished executing and before control returns to main
.
... When could e ever be out of scope?
Try referring to e
in main
after the call to func
. Does it compile?
No, because e
isn't an identifier in this scope.
What's the alternative? Every large program would accumulate every variable name that was ever used in the global namespace. It would rapidly become unmanageable, and in fact that's exactly the reason we use local variables instead of globals.
Note that we can disentangle scope from lifetime just by returning something. If we choose something with dynamic lifetime:
std::unique_ptr<Entity> foo()
{ // block begins
std::unique_ptr<Entity> e; // e scope begins
e.reset( new Entity ); // *e lifetime begins
e->Print();
return std::move(e); // *e moves to return value
} // e destroyed
int main()
{
auto f = foo(); // *e is now called *f
f->Print();
} // f destroyed and deletes *f
- You can see there's an anonymous object (created by
new
), which is pointed at first by *e
and then by *f
e
has the same (block) scope as before
e
has the same (automatic) lifetime, so it is destroyed when it goes out of scope
- the object originally known as
*e
is handed off to f
, and keeps existing. It still doesn't have an identifier of its own that could be bound to a scope.
f
also has block scope & automatic lifetime
*f
(the dynamic object formerly known as *e
) is destroyed along with f
only because we didn't move it elsewhere.