纸上得来终觉浅,绝知此事要躬行。
昨天和同事在讨论一段c++代码的时候发现一个有意思的事情,简单来说问题是这样的, 现在有a.h, a.cpp, b.cpp
a.h
#ifndef _A_H
#define _A_H
...
...
#endif
a.cpp
#include "a.h"
...
...
b.cpp
#include "a.h"
int main()
{
...
}
现在有这么一个问题,需要一个全局变量,考虑到在a.cpp和b.cpp中都要使用,所以就把这个变量定义在a.h中,编译的时候
没有error, 但是在链接的时候抛出多重定义的错误,考虑到a.h已经加了#program once, 按理在整个工程中不会被重复编译,后来
搜索后发现,原来编译的时候是分离的,也就是说编译a.cpp和b.cpp时二者是不可见的,在预编译的时候把include里面的代码
拷到对应的cpp文件中,然后各自编译成.o文件,最后再将两个.o文件链接一起生成exe文件,而在各自编译.o文件的过程中,是
不知道外面的情况的,而一旦链接的时候就会发现原来外面也定义了这个变量,为此就会报错。
对于#program once的作用,其实只是为了保证该头文件只被拷贝一次到对应的cpp文件,而不是说保证整个工程值拷贝一次。
具体的场景是,如果a.h中有#include<iostream>, 而我在b.cpp中也有#incldue<iostream>, 那么在编译b.cpp的时候就会拷贝两
次iostream.所以我们可以看到iostrean中会有#program once,这样就可以避免在一个文件(而非工程)中被include两次。
解决方案:
对于这中问题的解决方案也很简单,因为我们在头文件中定义了变量,所以在不同cpp编译的时候都生成该变量,为了阻止这
种事情发生,只需要把定义改为声明,声明就是extern int a, 然后只在某一个cpp(任何一个CPP)中对其声明就可以啦。
思考:
当工程很简单的时候,上述的问题我们基本不会遇到,因为单个cpp完全没有问题,但是对于大型工程就要非常注意,而面对
这样的问题最重要的就是养成头文件不放定义的好习惯,所有的定义都放在具体的cpp中,头文件只负责声明。