背景
在平时写程序中,我们常常需要使用全局变量,但是如果在头文件(.h文件)中声明变量(非const常量)时,若有多个.cpp 文件include这个头文件时会出现多次定义的错误。
如在a.h文件中:
// a.h 文件
#include<iostream>
int a ;
void f();
在a.cc文件中:
// a.cc 文件
#include "a.h"
void f() {
a = 2;
std::cout << "a.cc: " << a << std::endl;
}
在main.cc文件中
#include "a.h"
int main() {
std::cout << "main: " << a << std::endl;
f();
return 0;
}
编译会出错:multiple definition of "a"
。多次定义了a变量。
这里有两个知识点:1.编译时inlcude操作会将.h文件中全部内容包含到.cc文件。2.程序中可以有多个声明但是只能有一个定义。在a.h文件中的int a
是变量定义,include操作时多个.cc文件就包含了多个a变量的定义,导致编译出错。如果对c++程序如何编译运行感兴趣的可以看这篇文章How C++ Works: Understanding Compilation。
什么情况下可以在头文件声明变量呢?
情况一:变量写在.cc文件中,头文件使用"extern"关键字
如上面的代码可以改为:
// a.h 文件
#include<iostream>
extern int a;
void f();
在a.cc文件中:
// a.cc 文件
#include "a.h"
void f() {
a = 2;
std::cout << "a.cc: " << a << std::endl;
}
在main.cc文件中
#include "a.h"
int a;
int main() {
std::cout << "main: " << a << std::endl;
f();
return 0;
}
这样就能使用a这个全局变量了。
情况二:头文件变量使用const 关键字
// a.h 文件
#include<iostream>
const int a;
void f();
在a.cc文件中:
// a.cc 文件
#include "a.h"
void f() {
std::cout << "a.cc: " << a << std::endl;
}
在main.cc文件中
#include "a.h"
int main() {
std::cout << "main: " << a << std::endl;
f();
return 0;
}
这样是可以编译通过,运行正确的:
main: 2
a.cc: 2
这是因为const对象默认为文件的局部变量,只能被包含它的文件可见,不能被其他编译单元看到。所以不会出现重复定义的错误。
情况三:头文件变量使用static 关键字
代码如下
// a.h 文件
#include<iostream>
static int a;
void f();
在a.cc文件中:
// a.cc 文件
#include "a.h"
void f() {
a = 2;
std::cout << "a.cc: " << a << std::endl;
}
在main.cc文件中
#include "a.h"
int main() {
a = 1;
std::cout << "main: " << a << std::endl;
f();
std::cout << "main: " << a << std::endl;
return 0;
}
运行结果如下:
main: 1
a.cc: 2
main: 1
可以看到虽然程序可以编译运行成功,但是a变量的值却不是全局变量。f()函数对a的修改并没有影响到main函数中的a的值。
这是因为:静态变量存储在全局存储区,其作用域只在当前编译单元中生效。也就是说在a.cc文件中和main.cc文件中的a是两个不同的变量,所以在a.cc中的函数修改a的值时候并不影响main中的a的值。
参考文献
C++头文件如何正确定义全局变量
Declaring variables in header files C++ [duplicate]
Define constant variables in C++ header
How C++ Works: Understanding Compilation