什么是变量遮蔽(名称隐藏)?
每个代码块都会定义自己的作用域区域。因此,当嵌套块中出现与外层块同名的变量时,会发生什么情况?此时,嵌套变量会在二者共同的作用域范围内“隐藏”外层变量,这一现象称为名称隐藏(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
程序流程说明:
- 首先在外层块声明
apples
并初始化为 5,该变量在内层块可见,首次打印为 5。 - 接着在内层块再次声明同名变量
apples
,初始化为 0。从该定义点到内层块结束,标识符apples
均指内层变量,外层变量被隐藏。 - 将 10 赋给当前可见的
apples
(即内层变量),再次打印得到 10。 - 内层块结束时,内层
apples
被销毁,对外层变量无任何影响。 - 回到外层块后,外层
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 默认开启相关警告。