In this video, we'll learn a little bit more about c++ scope rules.
C++ uses scope rules to determine when and where an identifier can be used.
It uses static or lexical scoping, which means that
the scope is determined the same way you read a program.
C++ has two main scopes, local scope and global scope.
Local or block scope refers to the visibility of an identifier that's
been declared in a block that is a set of open or closed curly braces.
Function parameters also have block scope.
These identifiers are visible within the block in which they're declared.
I talked about this earlier in the course, but it's
important to understand it in the context of functions.
When a function is called, you can think of the
function as being activated.
And the function parameters are bound to storage.
They become alive, and their lifetime is while the function is executing.
Once the function completes, the function is deactivated,
and these variables and parameters are no longer alive.
That doesn't mean that they're somehow marked as unavailable.
It simply means that the compiler doesn't recognize them any
longer, and the storage they were bound to will likely be reused.
That means that the values of local variables are not
preserved between function calls.
If we have nested blocks, then statements in the inner blocks can
see identifiers declared in the outer blocks, but outer blocks cannot see
variables defined in the inner blocks.
However, there is one type of variable whose value is
preserved between function calls.
This is a static local variable.
It's a variable whose lifetime is the lifetime of the program,
but it's only visible to the statements in the function body.
These variables can come in very handy when you need to know a
previous value in a function without having to pass it in all the time.
Static local variables are only initialized once.
If no initializer is provided, they're set to 0.
Identifiers with global scope are identifiers that are declared
outside of any function or class.
These identifiers are visible to all parts of the program
following their declaration.
Best practice with identifiers with global scope is that it's
okay to use global constants, but global variables should be avoided.
Let's see scope rules in action in the IDE.
Okay, so now I'm in the CodeLite IDE, and I'm in the section 11 workspace
in the scope example project.
And here I've got an example program that just exercises a few of the scope
rules, and I'll walk through them.
They're very straightforward.
The nice thing about languages like c++ and other languages that use
static scoping is that the scoping is lexical, so it's as you read the code.
It's not something that's happening at run time on the call stack.
Languages that use dynamic scoping rules are a little
harder to follow in this sense.
So let's take a look at this example here.
I've got my main.
And I've got two local variables defined in main.
They're local two main because they're defined inside those brackets.
Okay, that's really important.
So what's the scoping deal all about?
Well, when I use a variable name, for example, I'm using num here,
which num am I talking about?
That's what scope is all about.
And the rule is really very simple.
First, you look inside your own scope, inside your local scope so here's
num I'm using it, which one is it.
Well, look inside your scope here.
And you'll see, it's this one right here, right.
That's the closest one within the scope that you're in.
So this is going to print 100.
Now block statements, just like we had here.
Block statements create a new level of scope.
And we're going to see this again when we use classes.
So every time that you create a block, you've got a new level of scope.
So notice here I'm declaring num again.
This is a different num now.
I'm using the same name on purpose.
So now when I use it here in the output statement.
Which num am I talking about?
Am I talking about this one here on line 36?
Or am I talking about the one on line 42?
Well, you look in your closest scope first, right.
You look for a local variable.
So this num here refers to this num right here, that's the variable.
So this is going to display 200.
What about this one here, num1.
The rule again that you follow look inside here first.
Do you see a num1 declared in here.
No, you don't.
Then you look out to your outer scope.
There it is, right here.
So that's the one I'm using and that'll display 500.
Okay, hopefully, that makes sense.
Now here we go.
We're using num again.
But now we've got two nums.
We've got the num here on line 36 and we've got the num here
on line 42, which one do we use?
Well, you look in your level of scope, your local variable.
You can't look into another block.
You notice how this block here could see out, so we could see num1.
That's perfectly normal.
First you look in your local scope then you look outwards.
You don't look inwards.
Okay, so this num refers to this guy right here, on line 36.
So this is going to display 100 as well.
We'll go through a few more examples down here.
But let's run this one first.
We're expecting 100 200 500 and 100.
So let me run this example.
And here we go, 100 200 500 and 100.
Just what we expected.
When you read about scope in books and on websites and tutorials,
it's really complicated when you first learn about it, local,
non-local, global and all this stuff.
But conceptually, it's pretty straightforward.
First you look local, then you look out, out, out as far as you
need to go until you find it.
If you don't find, it you have a compiler error, for sure.
All right, so let's look at this one here.
I'm calling a function called local example.
And I'm passing in a 10.
And let me show you what that function looks like.
It's right here, let me expand it a little.
So I'm calling this with a 10.
So I'm passing in a 10 by value.
So this x right here will be a 10, right.
I'm going to copy the 10.
I've got a num variable right here.
And I'm initializing it to a 1000.
So my output statement is using num.
Which num am I using?
Well, I've got two choices, right.
I can use this one on line 20, or I can use this one on line 11.
I always use the local variable.
In this case, I'm going to use this one, on line 20.
So this is going to display a 1000.
Then I'm setting num equal to x.
Okay, X is 10.
Which num am I talking about?
I'm talking about this num again, right.
So I'm going to set this to 10.
And I'm going to display num, which is again the 10.
So this will display a 1000 and 10.
Notice that I can't use num1 here because it's not within the scope,
that was defined inside main.
Okay, so if I run this, I expect 1000 and ten.
And let's try that out.
And here you go, local number is a 1000 and then local number is 10.
Suppose I call this again but I pass in a 20 into it.
Okay, what do you think is going to happen?
Well, let's go back here again.
In this case, I'm passing in a 20.
So x is going to get the 20.
I'm initializing this to a 1000.
Every time I call this function, that num will be initialized to a 1000.
So in this case, there's num.
I'm using this one right here again.
So this will display a 1000.
Now I'm setting that same variable to 20.
And then I'm displaying it right here.
So this is going to display a 1000 and 20.
Okay, so it's the same idea, and it's pretty straightforward.
Let me run this.
So we thought we saw a 1000 and 10.
Now we're see a 1000 and 20.
Okay, so now let's look at an example of a global variable.
You'll notice that way up here at the top of the program.
I want to scroll all the way up.
Let me close that function and expand this one.
This is what we've got right here.
We've got a global variable called num right here on line 11.
And I'm initializing that global variable to 300.
How do I know it's a global variable?
It's not declared inside any function.
It's not declared inside any class.
It's just sitting there all on its own right.
This is global to the entire program.
So in this case, I'm calling global example.
And which number do I use?
Well, what's the first thing you do?
You look in here.
Is there a variable called num defined in here?
No, there isn't.
Then you look outward.
And there it is.
So this displays 300.
Then I take num, and I multiply it by 2.
Okay, which num?
Same one.
So this will now become 600.
And I'm going to display 600.
Okay, now what if I call this function again.
And I'll call it twice in the main.
What if I call it again.
Well, the next time I call it, this variable still has 600 in it.
It's a global variable.
So the next time I call it, we look at num, num is 600 which I display.
I multiply it by 2.
Okay, so that's 1200 now.
And then I display it again.
So it's 1200 this time.
Notice the global variable does not get reinitialized every time, it
only gets initialized that one time when you're -- actually before main
is even called, that's initialized.
So here we're going to end up with 300 600 and then 600 and 1200.
So let me uncomment that one line out so we can call this twice, right here.
And let's run that, and you can see here we've got
300 600 and then 600 1200.
Just what we'd expect.
All right, so let's look at one more example here is
the static local example.
And I'll scroll up.
Let me close that down.
Now we mentioned what a static variable was.
A static variable is a variable that is defined locally.
In this case, this is a static local variable.
So num is visible in here.
But because we're using the static qualifier, what that
means is that this num variable will be initialized to 5000.
The first time this function is called.
After that, it retains its previous value.
Real different from the example we saw in local value which it
got reinitialized every time.
So let's say I call static local example the first time.
We're initializing num to 5000.
That only happens once.
Then I'm using it.
Which one, again I'm looking inside local scope first.
So that's going to display 5000 here.
Then I'm incrementing num by a 1000.
Okay, now it's 6000.
And then I'm displaying it again.
Well, now it's 6000.
Now suppose I call static local again.
Since this has already been initialized, it won't
be initialized again.
And it's going to retain its previous values.
So it's 6000 right now, here it is.
So if I display, it I'm going to get a 6000.
And then I'm going to increment it again by a 1000 and display it again.
So I'm going to get 7000.
And when I run this a third time, I'll end up with the 7000 I started
with and the 8000 that I end up with.
So as you can see it's pretty straightforward.
This is a real useful concept, the concept of static local variables
because you've got a number or a variable in this case that
behaves like a global variable, right, because it's retaining its
value, but its scope is local.
So you don't have the same problems that you have with global variables,
which means that they're visible throughout the program and any
piece of code could change it.
In this case, if this number variable right here gets a really strange value
the only place they could have gotten it from was inside this function.
And you know exactly where to look.
So that wraps up scope.
It's a pretty simple concept.
And it's -- again, think -- look local first then look out then look out then
look out until you hit global scope.
If you don't find that variable there, then you've got a compiler error.