If you're familiar with the basics of C++, you probably know
what local variables are and how they work.
Let's take a look at this example.
I declared a variable x inside of the main function and
then passed it to the printInt function.
This function will take this variable as a parameter
and print it out to the terminal.
Both of these variables, x and a, are local variables, but what does that mean?
Local variables only exist in a specific part of the code,
which means that their access is restricted to a specific scope.
In this example,
both of the functions have their own scope. Anything written
between a set of curly braces in C++ is considered as a block of
code, and every block has its own scope.
Everything you declare inside of this block of code will disappear
once the program reaches the closing curly brace.
So once you declare this local variable x,
you can use it inside of this block of code.
But once the main function is finished,
the storage that holds this variable is released. This is
known as scope‑based resource management,
which implies that resources will be released from memory
automatically once they go out of scope. The same rules apply for
this variable, a, inside of the printInt function. You may think that
this variable is not inside of the scope because we said that the
scope is contained by curly braces.
However, function parameters are also inside of the function scope,
otherwise they would be useless if we cannot use them inside of the function.
We could've also declared this variable here inside of the function's body.
But the reason we didn't is because we want to use this
variable as a parameter so that we can pass something in
when this function gets called,
just like we did here in the main function. Function parameters are
just local variables tied to the function's body.
Another example where we don't have to literally declare a variable inside of
the curly braces is the for loop. I will create a simple for loop, which goes
from 0 to 2, and prints out the iterator variable, i, in each iteration.
Let's compile this to see the output.
Okay, and this is exactly what we expected, 0, 1, and 2.
The variable, i, is incremented by one in each iteration
until this condition statement returns false.
But where is this variable i declared? Is it declared in the main
function scope or inside of this loop? The declaration is not
inside of the curly braces, so does that mean that the variable is
owned by the main function?
Let's test this by printing out the variable after the loop.
If I try to compile this, I will get an error.
It seems that the variable, i, was not declared
inside of the main scope after all.
This is another exception to the rule. Just like with parameter variables,
the variables declared inside of the initialization statement of the for
loop are also delegated to the scope of that loop.
If I really want this code to work,
I would have to declare this variable outside of the loop and then just
assign the 0 here. This would put the variable inside of the main scope, and
since this loop is defined inside of the main scope,
we are also able to access this variable inside of the loop. And if I
compile this code now, we can see that there are no errors.
This shows you that the scopes can also be nested. Scopes
defined inside of another scope can access resources from
the scope that surrounds them, but this is not true for the other way around.
If I put another set of curly braces around this declaration and loop,
this would create another block of code, another scope.
You can already see that my code editor is warning me that this variable does
not exist in this scope because this closing curly brace will clean up all of
the resources declared inside of this block of code.
You will often hear that C++ programmers discuss this scope‑based resource
management idea by mentioning something called RAII. RAII is a term coined by
the C++ creator, Bjarne Stroustrup, and it stands for resource acquisition is
initialization. Even Bjarne admitted that this naming is very bad for what he
was trying to convey, but the concept itself is mainly focused on resource
management for objects. These simple local variables with primitive values are
guaranteed to have automatic storage management. We are sure that when we exit
the scope, all of the resources will be released.
However, if a local variable contains some more complex object,
that means that when the object is instantiated,
its constructor will be called. RAII reminds us that we need to make
sure that everything that gets allocated inside of the constructor needs
to be deallocated inside of the destructor.
This is what resource acquisition is initialization means.
We will test this in the next module.
For example, these resources can also be something like opening a file,
which means that the stream to that file needs to be
closed inside of the destructor, something that scope cannot do automatically.
Another example is when you dynamically allocate memory on heap,
but we will see an example of this later,
once we cover what heap is and how we can use it to create smart pointers.
Local variables are most common,
but there are also global variables and variables
defined with the static keyword.
However, this is out of the scope of this lesson, and yes, pun intended.