一步一步讲起:
1、C++程序生成可执行文件的过程:源文件(.cpp),经过预编译,生成编译单元;编译单元,经过编译,生成目标文件(.obj);目标文件,经过连接,生成可执行文件。
2、预编译做的事情:对#include,#define进行文本替换。
3、C++支持单独编译,也就是对每个编译单元进行单独编译。这就要求,编译单元是一个自包含的文件,也就是说,编译单元生成目标文件的时候,不需要其他的信息。因此,在编译单元内部,所有使用的东西,必须要有定义,或者有声明。定义的意思是:这个东西就放在这里,当然可以使用。声明的意思是:我有这个东西,但是这个东西放在其他地方,这样的话,就可以使用声明的东西了。
4、可以多次声明,但只能一次定义(有例外)。为什么?
我可以重复说,我有这个东西,我有这个东西。但是我不能把一个东西放在多个地方。
5、声明只是说,我有这个东西,什么时候定位到这个东西呢?
编译的时候,声明我有这个东西,就可以用了。在后面连接的时候,会从其他的目标文件,找到这个东西。
6、可以多次声明,但只能一次定义,是有例外的。有些情况不能重复声明,那就是类的成员方法和静态数据成员。
7、可以多次声明,但只能一次定义,是有例外的。有些情况是允许重复定义的。为什么允许重复定义?
在编译单元中,在编译的时候,对于有些东西,可以说,我有这个东西,就可以使用,等到连接的时候,找到这个东西。但是对于有些东西,只有声明,说我有这个东西,是不行的,必须定义,才能使用。这样就出现了,在多个编译单元进行了重复定义。这样的情况有:
a、加 static 前缀的全局变量定义.如: static int x;
b、枚举类型的定义.如: enum Boolean {NO,YES };
c、类的定义. 如: class Point { int d_x; int d_y; ... };
d、内联函数的定义.如: inline int operator==(const Point& left,const Point&right) { ... }
e、union的定义.
f、名字空间中const常量定义
为什么只有声明是不够的呢?
这是因为编译的时候,只有这些信息是不够的,比如类,只有声明,是没法分配内存的,内联方法,只有声明,是没法展开进行文本替换的。
8、那么问题来了,对于上面的情况,在多个编译单元,都进行了定义,连接的时候,为什么没有重定义的错误呢?
这是因为,对于上面的情况,连接的时候,是内部链接的,也就是说,连接的时候,不到外面找相同的东西了。
9、上面说的是,在多个编译单元之间,进行了重复定义,连接时不会报错,是因为内部连接。那么,在一个编译单元之内,进行了重复定义呢?会出现什么情况?
答案是:编译错误。
10、在一个编译单元内,不能进行重复定义。那么,问题又来了,在预处理的时候,#include递归地进行文本替换,这种时候,就会出现重复定义,如何解决呢?
答案是:使用头文件保护符。
#ifndef XXX
#define XXX
#endif
这样,就避免了重复地替换,也就不会出现重复定义了。注意,头文件保护符只是保证在一个编译单元,不会重复定义。在多个编译单元,还可能会重复定义。
11、也就是说,在一个编译单元内,肯定不能重复定义,否则编译错误。在多个编译单元之间呢,对于内部连接的情况,允许重定义。对于外部连接的情况,不允许重复定义。
12、在多个编译单元之间,为了避免出现重复定义,有两种解决办法:
a、使用内部连接。
b、使用外部连接,并且使用不具名空间。使用不具名空间,也是外部连接,为什么连接的时候,不会报错呢?
这是因为,不具名空间,虽然是外部连接,但是可以认为不具名空间是个封装的东西,外部连接想连接也连接不到。推荐使用外部连接。