In programming, we can imagine our code being split into regions, sometimes referred to as scopes.
When we declare a variable in one region or scope, it may not necessarily be accessible to other scopes.
We have thus far been declaring our variables at the top of our files, outside of any function body. This scope is sometimes referred to as the global scope, also known as the file scope.
This is because, as we’ve seen, we’ve been able to freely access these variables anywhere within our file:
#include <iostream>
using namespace std;
int x{42};
void Log(){ cout << "x is " << x; }
int main(){
Log();
cout << "\nit's definitely " << x;
}
x is 42
it's definitely 42
But this is not the only place we can declare variables. For example, we can also create variables within the scope of a function, by placing it between the {
and }
of the function’s body.
Below, we’ve moved our x
variable into the scope of our Log
function. As a result, it is no longer in the global scope, and is therefore no longer accessible by our main
function.
As such, our code throws a compilation error:
#include <iostream>
using namespace std;
void Log(){
int x{42};
cout << "x is " << x;
}
int main(){
Log();
cout << "\nit's definitely " << x;
}
error: 'x': undeclared identifier
Block scopes are created when we have a pair of curly braces {}
in our code. For example, function bodies create a block scope, as we’ve seen above.
This concept isn't limited to function bodies - for example, if
and else
statements can also create a block scope. Let's see an example:
#include <iostream>
using namespace std;
int main() {
bool shouldLog{true};
if (shouldLog) {
string Message{"Hello World"};
}
cout << Message;
}
error: `Message`: undeclared identifier
In this code, the variable Message
is declared within the if
statement's block. Consequently, Message
only exists within this block and is inaccessible outside of it.
When we try to use Message
outside of its block (here, in the cout
statement), the compiler throws an error. This is because Message
is not recognized outside its defining block.
Block scopes do not need to be associated with functions or if
statements - we can open a set of curly braces any time we want, thereby creating a new scope.
It’s somewhat uncommon to do this, but it highlights the key point:
#include <iostream>
using namespace std;
int main() {
{
string Message{"Hello World"};
}
cout << Message;
}
error: `Message`: undeclared identifier
Function parameters are scoped to the block of the function for which they are defined. That is, they have the same scope as if they had been variables declared within the function body.
In C++, the scope access rules govern how different parts of a program can access variables.
The key rule is: a scope can access variables from its parent scopes, but not from its child scopes. The previous errors were all caused by code in a scope trying to access a variable in a nested, or child scope.
Previously, we’ve been writing code in function scopes that access variables in parent scopes (typically, the global scope). As we’ve seen, that has been successful:
#include <iostream>
using namespace std;
string Message{"Hello World"};
int main(){
// Accessing variable in parent scope
cout << Message;
}
Hello World
Below, we show another variation of a child scope successfully accessing a variable in a parent scope:
#include <iostream>
using namespace std;
int main(){
bool shouldLog{true};
string Message{"Hello World"};
if (shouldLog) {
// Accessing variable in parent scope
cout << Message;
}
}
Hello World
Here, Message
is declared in the main
function's scope. The if
statement within main
represents a child scope, nested within the main
function’s scope.
As such, variables declared in main
(parent scope) can be accessed in the if
statement (child scope).
What will the following function return?
int GetInt() {
int x{1};
{ x++; }
return x;
}
What will the following function return?
int GetInt() {
{
int x{1};
x++;
}
return x;
}
Shadowing or name hiding in C++ occurs when two variables in different scopes have the same name.
The variable in the inner scope "shadows" the variable in the outer scope, making the outer variable inaccessible within the inner scope. Here's an example to demonstrate this concept:
#include <iostream>
using namespace std;
int main(){
int x{1};
{
// This 'x' shadows the outer 'x'
int x{2};
cout << "Inner x: " << x; // Outputs 2
}
cout << "\nOuter x: " << x; // Outputs 1
}
Inner x: 2
Outer x: 1
In this example, there are two variables named x
. The x
inside the block is separate and shadows the x
declared in the main
function.
Within the block, any reference to x
refers to the inner x
(with value 2), not the outer x
(with value 1). When the block ends, the inner x
goes out of scope, and the outer x
becomes accessible again.
Shadowing is typically something we want to avoid, as it can lead to confusion and bugs. However, it’s still important to understand what’s going on here, so we can ensure we’re accessing the correct variables.
What will the following function return?
int GetInt() {
int x{1};
{
int x{2};
}
return x;
}
Right now, the rules and complexes around scopes might seem a bit much, especially given how simple our programs are.
As our projects become more complex, we’ll come to appreciate and embrace scoping to make our program more manageable.
We'll soon learn to use the scope resolution operator and namespaces, which help access and group code across different scopes, helping us with this task.
In this lesson on scope in C++, you've learned several key concepts:
{}
are only accessible within those braces.In the next lesson, we will delve into the concept of forward declaration, focusing on its application in functions.
Forward declaration allows you to inform the compiler about the existence of an entity (like a function) before you provide its complete definition.
This technique can help in organizing and structuring your code more effectively. We'll explore:
Learn more about how and when we can access variables, by understanding the importance of the scope in which they exist.
Become a software engineer with C++. Starting from the basics, we guide you step by step along the way