2.2.4 名字的作用域
不论是在程序的什么位置,使用到的每个名字都会指向一个特定的实体:变量、函数、类型等。然而,同一个名字如果出现在程序的不同位置,也可能指向的是不同实体。
作用域(scope)是程序的一部分,在其中名字有其特定的含义。C++(www.cppentry.com)语言中大多数作用域都以花括号分隔。
同一个名字在不同的作用域中可能指向不同的实体。名字的有效区域始于名字的声明语句,以声明语句所在的作用域末端为结束。
一个典型的示例来自于1.4.2节(第13页)的程序:
- #include <iostream>
- int main()
- {
- int sum = 0;
- // sum用于存放从1到10所有数的和
- for (int val = 1; val <= 10; ++val)
- sum += val; // 等价于sumsum = sum + val
- std::cout << "Sum of 1 to 10 inclusive is "
- << sum << std::endl;
- return 0;
- }
这段程序定义了3个名字:main、sum和val,同时使用了命名空间名字std,该空间提供了2个名字cout和cin供程序使用。
名字main定义于所有花括号之外,它和其他大多数定义在函数体之外的名字一样拥有全局作用域(global scope)。一旦声明之后,全局作用域内的名字在整个程序的范围内都可使用。名字sum定义于main函数所限定的作用域之内,从声明sum开始直到main函数结束为止都可以访问它,但是出了main函数所在的块就无法访问了,因此说变量sum拥有块作用域(block scope)。名字val定义于for语句内,在for语句之内可以访问val,但是在main函数的其他部分就不能访问它了。
建议:当你第一次使用变量时再定义它
一般来说,在对象第一次被使用的地方附近定义它是一种好的选择,因为这样做有助于更容易地找到变量的定义。更重要的是,当变量的定义与它第一次被使用的地方很近时,我们也会赋给它一个比较合理的初始值。
嵌套的作用域
作用域能彼此包含,被包含(或者说被嵌套)的作用域称为内层作用域(inner scope),包含着别的作用域的作用域称为外层作用域(outer scope)。
作用域中一旦声明了某个名字,它所嵌套着的所有作用域中都能访问该名字。同时,允许在内层作用域中重新定义外层作用域已有的名字:
- #include <iostream>
- // 该程序仅用于说明: 函数内部不宜定义与全局变量同名的新变量
- int reused = 42; // reused拥有全局作用域
- int main()
- {
- int unique = 0; // unique拥有块作用域
- // 输出#1: 使用全局变量reused;输出 42 0
- std::cout << reused << " " << unique << std::endl;
- int reused = 0; // 新建局部变量reused,覆盖了全局变量reused
- // 输出#2: 使用局部变量reused; 输出0 0
- std::cout << reused << " " << unique << std::endl;
- // 输出#3: 显式地访问全局变量reused; 输出 42 0
- std::cout << ::reused << " " << unique << std::endl;
- return 0;
- }
输出#1出现在局部变量reused定义之前,因此这条语句使用全局作用域中定义的名字reused,输出42 0。输出#2发生在局部变量reused定义之后,此时局部变量reused正在作用域内(in scope),因此第二条输出语句使用的是局部变量reused而非全局变量,输出0 0。输出#3使用作用域操作符(参见1.2节,第8页)来覆盖默认的作用域规则,因为全局作用域本身并没有名字,所以当作用域操作符的左侧为空时,向全局作用域发出请求获取作用域操作符右侧名字对应的变量。结果是,第三条输出语句使用全局变量reused,输出42 0。
如果函数有可能用到某全局变量,则不宜再定义一个同名的局部变量。
2.2.4节练习
练习2.13:下面程序中j的值是多少?
- int i = 42;
- int main()
- {
- int i = 100;
- int j = i;
- }
练习2.14:下面的程序合法吗?如果合法,它将输出什么?
- int i = 100, sum = 0;
- for (int i = 0; i != 10; ++i)
- sum += i;
- std::cout << i << " " << sum << std::endl;