C++允许声明一些不属于任何类的函数和变量,并且这些函数和变量可以被其他的任意函数访问。我们已经看到了多个全局函数的例子,包括作为程序入口的main()函数。全局变量还没有看到几个,因为它们需要在程序的模块和线程之间来回重复以求取折中。但理解它们还是很重要的。因为你可能在那些C程序员高手和其他的C++用户那里遇到它们。
为了能够举例说明全局函数和全局变量是如何工作,我们将会从研究一个小程序开始,该程序使用quick-and-dirty算法打印一个由128个伪随机数构成的列表。这个程序的源代码放在两个.cpp文件中。
第一个.cpp文件的源文件是random.cpp:
int randomNumbers[128];
static int seed = 42;
static int nextRandomNumber()
{
seed = 1009 + (seed * 2011);
return seed;
}
void populateRandomArray()
{
for(int i = 0; i < 128; i++)
{
randomNumbers[i] = nextRandomNumber();
}
}
这个文件声明了两个全局变量(randomNumbers和seed)和两个全局函数[nextrandomNumber()和populateRandomArray()]。其中的两个声明包含了关键字static,这样的声明只有在当前编译单元(random.cpp)中才是可见的,可以把这种情况称为静态连接(static linkage)。其他两个则可以从程序的任意编译单元中访问,可以把这种情况称为外部连接(external linkage)。
静态连接非常适合用于那些不需要在其他编译单元中使用的帮助函数和内部变量。它可以降低标识符(具有同样名字的全局变量或者是在不同编译单元中具有同样署名的全局函数)冲突的风险,并且可以防止那些不怀好意或者是考虑不成熟的用户访问一个编译单元的内部。
现在,让我们一起来看一看第二个文件,main.cpp,它使用了在random.cpp文件中用外部连接声明的两个全局变量:
#include <iostream>
extern int randomNumber[128];
void populateRandomArray();
int main()
{
populateRandomArray();
for(int i = 0; i < 128; i++)
{
std::cout<<randomNumbers[i] <<std::endl;
}
return 0;
}
在调用外部变量和函数之前,需要先声明它们。对randomNumbers的外部变量声明(可以让一个外部变量在当前编译单元中可见)以extern关键字开始。没有extern,编译器就会认为它需要处理的是一个不确定的定义,这样就会导致连接器报错,因为同一变量同时在两个编译单元(random.cpp和main.cpp)中都被定义了。只要需要,可以任意多次地声明变量,但是只能定义它们一次。定义(definition)就是让编译器为该变量保留内存空间。
populateRandomArray()函数是通过使用函数原型来声明的。对于函数,extern关键字是可有可无的。
通常情况下,会把external变量和函数声明放在头文件中,并且把该文件在所有需要它们的文件中包含一次:
#ifndef RANDOM_H
#define RANDOM_H
extern int randomNumbers[128];
void populateRandomArray();
#endif
我们已经看到了如何使用static来声明一些不属于任何一个类实例的成员变量和成员函数,并且现在也已经看到了如何使用它来声明静态连接的函数和变量。static关键字还有另外一种用法不能忘记。在C++中,可以声明一个局部静态变量(local static variable)。这样的变量会在第一次调用函数的时候得到初始化,并且会在两个函数调用时保留它们的值。例如:
void nextPrime()
{
static int n = 1;
do
{
n++;
}while(!isPrime(n));
return n;
}
局部静态变量除了只能在定义它们的函数中可见之外,在其他方面,它们都与全局变量一样。