C++变量遮蔽(名称隐藏)详解与作用域规则

什么是变量遮蔽(名称隐藏)?

每个代码块都会定义自己的作用域区域。因此,当嵌套块中出现与外层块同名的变量时,会发生什么情况?此时,嵌套变量会在二者共同的作用域范围内“隐藏”外层变量,这一现象称为名称隐藏(name hiding)或变量遮蔽(shadowing)。

局部变量的遮蔽

#include <iostream>

int main()
{ // 外层块
    int apples { 5 }; // 外层块的 apples

    { // 嵌套块
        // 这里的 apples 指外层 apples
        std::cout << apples << '\n'; // 输出 5

        int apples { 0 }; // 在嵌套块作用域内重新定义 apples

        // 现在 apples 指嵌套块的 apples,外层 apples 被暂时隐藏

        apples = 10; // 将 10 赋给嵌套块的 apples,而非外层的

        std::cout << apples << '\n'; // 输出 10
    } // 嵌套块 apples 被销毁

    std::cout << apples << '\n'; // 再次输出外层 apples,仍为 5
    return 0;
} // 外层 apples 被销毁

运行结果:

5
10
5

程序流程说明:

  1. 首先在外层块声明 apples 并初始化为 5,该变量在内层块可见,首次打印为 5。
  2. 接着在内层块再次声明同名变量 apples,初始化为 0。从该定义点到内层块结束,标识符 apples 均指内层变量,外层变量被隐藏。
  3. 将 10 赋给当前可见的 apples(即内层变量),再次打印得到 10。
  4. 内层块结束时,内层 apples 被销毁,对外层变量无任何影响。
  5. 回到外层块后,外层 apples 重新可见,打印结果仍为 5。

若内层块未重新定义 apples,则内层块中的 apples 始终指外层变量,赋值 10 将作用于外层变量:

#include <iostream>

int main()
{
    int apples { 5 };

    {
        std::cout << apples << '\n'; // 5
        apples = 10;                 // 作用于外层 apples
        std::cout << apples << '\n'; // 10
    }
    std::cout << apples << '\n';     // 10
    return 0;
}

输出:

5
10
10

在内层块中,无法直接访问被隐藏的外层变量。

全局变量的遮蔽

与嵌套块遮蔽外层局部变量类似,当局部变量与全局变量同名时,局部变量会在其作用域内遮蔽全局变量:

#include <iostream>

int value { 5 }; // 全局变量

void foo()
{
    std::cout << "global variable value: " << value << '\n'; // 使用全局 value
}

int main()
{
    int value { 7 }; // 局部变量,遮蔽全局 value

    ++value; // 递增局部 value

    std::cout << "local variable value: " << value << '\n';
    foo();
    return 0;
}

输出:

local variable value: 8
global variable value: 5

由于全局变量位于全局命名空间,可使用无前缀的作用域解析运算符 :: 来指定全局变量:

#include <iostream>

int value { 5 }; // 全局变量

int main()
{
    int value { 7 };
    ++value;                          // 递增局部 value
    --(::value);                      // 递减全局 value(括号仅提高可读性)

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

输出:

local variable value: 8
global variable value: 4

避免变量遮蔽

局部变量遮蔽容易导致误用或修改错误变量,应尽量避免;部分编译器会在发生遮蔽时发出警告。
为避免全局变量被遮蔽,可为全局变量统一添加 g_ 前缀。

最佳实践
避免变量遮蔽。
若使用 GCC 或 Clang,可添加 -Wshadow 选项以产生遮蔽警告;Visual Studio 默认开启相关警告。

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"