7-5 变量遮蔽(名称隐藏)

每个代码块都定义自己的作用域区域。那么当嵌套代码块中的变量与外部代码块中的变量同名时会发生什么?此时,嵌套变量会在两者同时处于作用域内的区域中“隐藏”外部变量。这种现象称为名称隐藏name hiding遮蔽shadowing


局部变量的遮蔽

#include <iostream>

int main()
{ // outer block
    int apples { 5 }; // here's the outer block apples

    { // nested block
        // apples refers to outer block apples here
        std::cout << apples << '\n'; // print value of outer block apples

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

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

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

        std::cout << apples << '\n'; // print value of nested block apples
    } // nested block apples destroyed


    std::cout << apples << '\n'; // prints value of outer block apples

    return 0;
} // outer block apples destroyed

运行此程序时,它会输出:

image

在上面的程序中,我们首先在外层代码块中声明了一个名为apples的变量。该变量在内层代码块中可见,通过打印其值(5)可以验证这一点。随后我们在嵌套代码块中声明了另一个同名变量(同样命名为apples)。从此时起直至代码块结束,名称apples都指向嵌套代码块中的apples,而非外层代码块中的apples。

因此,当我们将值10赋给apples时,实际赋值对象是嵌套块中的apples。打印该值(10)后,嵌套块结束且嵌套块apples被销毁。外层块apples的存在与值不受影响,通过打印外层块apples的值(5)可验证此结论。

需注意:若未定义嵌套块 apples,嵌套块中的 apples 仍将指向外层块 apples,此时将 10 赋值给 apples 操作将作用于外层块 apples:

#include <iostream>

int main()
{ // outer block
    int apples{5}; // here's the outer block apples

    { // nested block
        // apples refers to outer block apples here
        std::cout << apples << '\n'; // print value of outer block apples

        // no inner block apples defined in this example

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

        std::cout << apples << '\n'; // print value of outer block apples
    } // outer block apples retains its value even after we leave the nested block

    std::cout << apples << '\n'; // prints value of outer block apples

    return 0;
} // outer block apples destroyed

上述程序输出:

image

在嵌套代码块内部时,无法直接访问外部代码块中被遮蔽的变量。


全局变量的遮蔽

类似于嵌套代码块中的变量可以遮蔽外部代码块中的变量,与全局变量同名的局部变量将在其作用域内遮蔽全局变量:

#include <iostream>
int value { 5 }; // global variable

void foo()
{
    std::cout << "global variable value: " << value << '\n'; // value is not shadowed here, so this refers to the global value
}

int main()
{
    int value { 7 }; // hides the global variable value (wherever local variable value is in scope)

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

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

    foo();

    return 0;
} // local value is destroyed

此代码输出:

image

然而,由于全局变量属于全局命名空间的一部分,我们可以使用范围运算符(::)而不加前缀,以此告知编译器我们指的是全局变量而非局部变量。

#include <iostream>
int value { 5 }; // global variable

int main()
{
    int value { 7 }; // hides the global variable value
    ++value; // increments local value, not global value

    --(::value); // decrements global value, not local value (parenthesis added for readability)

    std::cout << "local variable value: " << value << '\n';
    std::cout << "global variable value: " << ::value << '\n';

    return 0;
} // local value is destroyed

此代码输出:

image


避免变量遮蔽

通常应避免局部变量遮蔽,否则可能导致意外错误——即使用或修改了错误的变量。部分编译器在变量被遮蔽时会发出警告。

基于避免局部变量遮蔽的相同原因,我们同样建议避免全局变量遮蔽。若所有全局名称均使用“g_”前缀,则可轻松规避此问题。

最佳实践
避免变量遮蔽。

针对 gcc 用户
GCC 和 Clang 支持 -Wshadow 选项,当变量被遮蔽时会触发警告。该选项包含多个子变体(-Wshadow=global、-Wshadow=local 和 -Wshadow=compatible-local)。具体差异请参阅 GCC 文档说明。
Visual Studio 默认启用此类警告。

posted @ 2026-02-22 10:15  游翔  阅读(0)  评论(0)    收藏  举报