2.6 初识名字查找
2025年2月23日大约 3 分钟
问题的引入
在前面作用域的介绍中,有这样的一段示例代码:
int sub_mul(int a, int b, int c) {
int diff = a - b;
return diff * c;
}
a = 1; // 错误:a在这里都是不可见的
但是,如果外面有一个全局变量 a
,那么这段代码就是正确的:
int a = 1; // 全局变量 a
int sub_mul(int a, int b, int c) {
int diff = a - b;
return diff * c;
}
a = 1; // 正确:这里被访问的是全局变量 a
实际上,对于所有嵌套的作用域,内部的名字都会覆盖外部作用域中的名字。例如:
int a = 1; // a1
{
int a = 2; // a2
{
int a = 3; // a3
a += 1; // 在这里,a 是内部的 a3
}
a += 1; // 在这里,a 是 a2
}
a += 1; // 在这里,a 是 a1
这样内部可以覆盖外部的设计,让局部代码不需要关心和全局变量的冲突,显著减少了程序员的心智负担。但是,考虑如下的情况:
int a = 1;
{
int a = 2;
{
a += 1; // 这里的 a 是哪个呢?
}
}
这里的 a
是哪个呢?
非限定名字查找
以目前讲述过的概念而言,读者可以用下面的规则来理解名字查找的过程:
- 声明必须在使用之前。
- 从下往上查找,在当前作用域找不到的话,到外层作用域继续向上查找。
- 如果一个名字找到至少一个对应的声明,就停止查找。
例如,前面的问题中:
int a = 1; // a1
{
int a = 2; // a2
{
a += 1; // 这里的 a 是 a2
}
}
名字查找只会向上查找,在如下的代码中:
int a = 1; // a1
{
{
a += 1; // 这里的 a 是 a1
int a = 2; // a2
}
int a = 3; // a3
}
虽然后面的两个 a
在更内层的作用域中声明,但是在使用的时候,查找的过程是从下往上的,所以这里 a += 1;
中的 a
是外层的 a
。
:::
相关信息
这里的标题是非限定名字查找,是因为还有一种查找称为限定名字查找,这种查找形式如下:
namespace ns {
int a = 1;
}
ns::a = 2; // 限定名字查找
这会在后面的章节中介绍。
相关信息
找到至少一个对应的声明就停止查找 仅用于变量的名字查找。函数如果找到了名字相同的声明,会继续查找,并将所有匹配的函数声明放在一起,组成一个重载集。重载集的概念会在后面的章节中介绍。
相关信息
在编程语言刚开始发展的时期,作用域的概念还不完善。那个时候甚至有任何名字都不能重复的要求。但随着程序的复杂度增加,这样的要求变得不切实际。要求某个组件的开发者熟悉整个项目的命名,小心翼翼地避免冲突带来的开发成本是巨大的。
读者可能在一些资料上看到了这样的要求,但是在现代的编程语言中,这样的要求已经不再存在。