Variable Shadowing

When a variable with the same name is declared inside a nested block, it can “hide” or “shadow” a variable with the same name in an outer block. This phenomenon is known as name hiding or shadowing.

What is Variable Shadowing❔

Variable shadowing occurs when a variable declared in an inner scope (like a block or a function) has the same name as a variable declared in an outer scope. The inner variable "shadows" the outer variable, making the outer variable inaccessible within the inner scope.

Example 1:

#include <iostream>

int main() {
    // Outer block
    int apples { 5 }; // Outer block apples

    { // Nested block
        // Inner block apples refers to outer block apples
        std::cout << apples << '\n'; // Output: 5

        int apples{ 0 }; // Define apples in the scope of the nested block

        // Inner block apples now refers to the nested block apples
        // The outer block apples is temporarily hidden

        apples = 10; // Assign value 10 to nested block apples, not outer block apples

        std::cout << apples << '\n'; // Output: 10
    } // Nested block apples destroyed

    std::cout << apples << '\n'; // Output: 5 (Outer block apples remains unaffected)

    return 0;
} // Outer block apples destroyed

In this example, a variable named apples is declared in both the outer and nested blocks. The inner block's apples temporarily hides the outer block's apples within its scope.

Example 2:

#include <iostream>

int main() {
    // Outer block
    int apples { 5 }; // Outer block apples

    { // Nested block
        // Inner block apples refers to outer block apples
        std::cout << apples << '\n'; // Output: 5

        // No inner block apples defined in this example

        apples = 10; // This applies to outer block apples

        std::cout << apples << '\n'; // Output: 10
    } // Outer block apples retains its value even after leaving the nested block

    std::cout << apples << '\n'; // Output: 10 (Outer block apples remains unaffected)

    return 0;
} // Outer block apples destroyed

In this example, since there is no inner block variable apples defined, the name apples within the nested block still refers to the outer block's apples.

Shadowing of Global Variables

#include <iostream>

int value { 5 }; // Global variable

void foo() {
    std::cout << "Global variable value: " << value << '\n'; // Global value is not shadowed here
}

int main() {
    int value { 7 }; // Hides the global variable value until the end of this block

    ++value; // Increments local value, not global value

    std::cout << "Local variable value: " << value << '\n';

    foo();

    return 0;
} // Local value is destroyed

In this example, a local variable value inside the main function shadows the global variable value within its scope. The global variable is accessible using the scope operator (::).

1 Shadowing Hierarchy:

  • When accessing a variable, the compiler looks for the nearest declaration first (inner scope) and then moves outward (outer scope).

2 Avoiding Shadowing:

  • To prevent confusion, it’s best to avoid naming variables in inner scopes the same as those in outer scopes. Use meaningful names to increase code readability.

3 Use of :: Operator:

  • In C++, you can access the outer variable explicitly using the scope resolution operator ::, especially if you want to refer to the outer variable inside the inner scope.
#include <iostream>

using namespace std;

int var = 7;

int main()
{
    int var = 1;
    
    cout << ::var;

}
// Output

7

Best Practices

1 Avoid Variable Shadowing:

  • Shadowing of local and global variables should generally be avoided, as it can lead to inadvertent errors where the wrong variable is used or modified.

2 Use Meaningful Prefixes:

  • Consider using meaningful prefixes, such as “g_” for global variables, to differentiate them from local variables. This practice enhances code readability and helps avoid shadowing issues.

3 Limit Scope:

  • Keep variable scopes as small as possible. Use local variables within functions or blocks to reduce the likelihood of shadowing.
void process() {
    int result;  // Limited to this function
}

4 Explicitly Reference Outer Variables:

  • If you need to access a shadowed variable, use the scope resolution operator :: to refer explicitly to the outer variable.
std::cout << "Global count: " << ::globalCounter << std::endl;