概述:在上一篇我们知道 通过 #ifndef....#defin....#endif , 这个解决头文件重复包含的问题
C++:重定义:class类型重定义_hongwen_yul的博客-CSDN博客
避免头文件的重复包含可以有效的避免变量的重复定义,其实不光变量可以避免重复定义,也可以避免函数和类、结构体的重复定义。但是避免头文件的重复包含是否一定可以避免 变量、函数、类、结构体的重复定义了 ?答案当然是否定的,下面我们通过GCC工具来探究 “C++ 预处理---》编译----》链接” 这几个过程。
Windows下GCC安装和使用_丸子爱学习!的博客-CSDN博客_windows gcc
gcc 常见的命令,参照下面这篇文章
C++:GCC编译:GCC编译C++程序分步流程_hongwen_yul的博客-CSDN博客
- a.h
- int A = 2;
- b.h
- #include
- #include"a.h"
- void fb();
-
-
- b.cpp
- #include"b.h"
- void fb() {
- printf("%d", A+1);
- }
- c.h
-
- #pragma once
- #include
- #include"a.h"
- void fc();
-
-
-
- c.cpp
- #include"c.h"
- void fc() {
- printf("%d", A + 2);
- }
- main.cpp
-
- #include
- #include"b.h"
- #include"c.h"
-
- #include
- int main() {
- fb();
- fc();
- return 0;
- }
-
-
- // a.h b.h c.h 这三个头文件没有添加 #ifndef....#defin....#endif 情况下
- // 通过 gcc -E main.cpp 预编译结果
-
- # 2 "main.cpp" 2
- # 1 "b.h" 1
-
- # 1 "a.h" 1
-
- # 1 "a.h"
- int A = 2; // 第一次定义变量 A
- # 3 "b.h" 2
- void fb();
- # 3 "main.cpp" 2
- # 1 "c.h" 1
-
- # 1 "a.h" 1
- int A = 2; // 第二次定义变量 A
- # 3 "c.h" 2
- void fc();
- # 4 "main.cpp" 2
-
-
- int main() {
- fb();
- fc();
- return 0;
- }
-
-
- // 很明显 预编译之后的结果:main.cpp 会定义两次 变量 A ,所以通过 gcc -C main.cpp 就会出现变量重复定义问题
-
- $ gcc -C main.cpp
- In file included from c.h:2:0,
- from main.cpp:3:
- a.h:1:5: error: redefinition of 'int A'
- int A = 2;
- ^
- In file included from b.h:2:0,
- from main.cpp:2:
- a.h:1:5: note: 'int A' previously defined here
- int A = 2;
- ^
-
-
可以看到main.cpp 编译后没有任何影响,从而避免了重复包含的问题
- a.h
-
- #pragma once
- int A = 2;
-
-
- ===================================================================================
-
- b.h
- #pragma once
- #include
- #include"a.h"
- void fb();
-
-
- b.cpp
- #include"b.h"
- void fb() {
- printf("%d", A+1);
- }
-
- ==========================================================================================
-
- c.h
- #pragma once
- #include
- #include"a.h"
- void fc();
-
- c.cpp
- #include"c.h"
- void fc() {
- printf("%d", A + 2);
- }
- =========================================================================================
-
- main.cpp
-
- #include
- #include"b.h"
- #include"c.h"
-
- #include
- int main() {
- fb();
- fc();
- return 0;
- }
- // gcc -E main.cpp 预编译结果
-
- # 2 "main.cpp" 2
- # 1 "b.h" 1
-
-
- # 1 "a.h" 1
-
-
- # 2 "a.h"
- int A = 2;
- # 4 "b.h" 2
- void fb();
- # 3 "main.cpp" 2
- # 1 "c.h" 1
-
-
-
- void fc();
- # 4 "main.cpp" 2
-
-
- int main() {
- fb();
- fc();
- return 0;
- }
-
- // gcc -C -S main.cpp 编译的结果
-
- .file "main.cpp"
- .section .rdata,"dr"
- __ZStL19piecewise_construct:
- .space 1
- .globl _A
- .data
- .align 4
- _A:
- .long 2
- .def ___main; .scl 2; .type 32; .endef
- .text
- .globl _main
- .def _main; .scl 2; .type 32; .endef
- _main:
- LFB935:
- .cfi_startproc
- pushl %ebp
- .cfi_def_cfa_offset 8
- .cfi_offset 5, -8
- movl %esp, %ebp
- .cfi_def_cfa_register 5
- andl $-16, %esp
- call ___main
- call __Z2fbv
- call __Z2fcv
- movl $0, %eax
- leave
- .cfi_restore 5
- .cfi_def_cfa 4, 4
- ret
- .cfi_endproc
- LFE935:
- .ident "GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"
- .def __Z2fbv; .scl 2; .type 32; .endef
- .def __Z2fcv; .scl 2; .type 32; .endef
- a.h
- #pragma once
- int A = 2;
-
- ==================================================================================
- b.h
-
- #pragma once
- #include
- #include"a.h"
- void fb();
-
- b.cpp
- #include"b.h"
- void fb() {
- printf("%d", A+1);
- }
-
- // 预编译 b.cpp
- gcc -E b.cpp
-
- # 3 "b.h" 2
- # 1 "a.h" 1
-
-
- # 2 "a.h"
- int A = 2;
- # 4 "b.h" 2
- void fb();
- # 2 "b.cpp" 2
- void fb() {
- printf("%d", A+1);
- }
-
- // 编译 gcc -C -S b.cpp -o b.txt
-
- .file "b.cpp"
- .section .rdata,"dr"
- __ZStL19piecewise_construct:
- .space 1
- .globl _A
- .data
- .align 4
- _A:
- .long 2
- .section .rdata,"dr"
- LC0:
- .ascii "%d\0"
- .text
- .globl __Z2fbv
- .def __Z2fbv; .scl 2; .type 32; .endef
- __Z2fbv:
- LFB935:
- .cfi_startproc
- pushl %ebp
- .cfi_def_cfa_offset 8
- .cfi_offset 5, -8
- movl %esp, %ebp
- .cfi_def_cfa_register 5
- subl $24, %esp
- movl _A, %eax
- addl $1, %eax
- movl %eax, 4(%esp)
- movl $LC0, (%esp)
- call _printf
- nop
- leave
- .cfi_restore 5
- .cfi_def_cfa 4, 4
- ret
- .cfi_endproc
- LFE935:
- .ident "GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"
- .def _printf; .scl 2; .type 32; .endef
- ====================================================================================
-
- c.h
- #pragma once
- #include
- #include"a.h"
- void fc();
-
- c.cpp
- #include"c.h"
- void fc() {
- printf("%d", A + 2);
- }
-
- // 预编译 gcc -E c.cpp
-
- # 3 "c.h" 2
- # 1 "a.h" 1
-
-
- # 2 "a.h"
- int A = 2;
- # 4 "c.h" 2
- void fc();
- # 2 "c.cpp" 2
-
- void fc() {
- printf("%d", A + 2);
- }
-
- // 编译 gcc -C -S c.cpp -o c.txt
- .file "c.cpp"
- .section .rdata,"dr"
- __ZStL19piecewise_construct:
- .space 1
- .globl _A
- .data
- .align 4
- _A:
- .long 2
- .section .rdata,"dr"
- LC0:
- .ascii "%d\0"
- .text
- .globl __Z2fcv
- .def __Z2fcv; .scl 2; .type 32; .endef
- __Z2fcv:
- LFB935:
- .cfi_startproc
- pushl %ebp
- .cfi_def_cfa_offset 8
- .cfi_offset 5, -8
- movl %esp, %ebp
- .cfi_def_cfa_register 5
- subl $24, %esp
- movl _A, %eax
- addl $2, %eax
- movl %eax, 4(%esp)
- movl $LC0, (%esp)
- call _printf
- nop
- leave
- .cfi_restore 5
- .cfi_def_cfa 4, 4
- ret
- .cfi_endproc
- LFE935:
- .ident "GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"
- .def _printf; .scl 2; .type 32; .endef
-
-
- ================================================================================
- main.cpp
-
- #include
- #include"b.h"
- #include"c.h"
-
- #include
- int main() {
- fb();
- fc();
- return 0;
- }
-
- // 预编译 gcc -E main.cpp
- # 2 "main.cpp" 2
- # 1 "b.h" 1
-
-
- # 1 "a.h" 1
-
-
- # 2 "a.h"
- int A = 2;
- # 4 "b.h" 2
- void fb();
- # 3 "main.cpp" 2
- # 1 "c.h" 1
-
-
-
- void fc();
- # 4 "main.cpp" 2
-
-
- int main() {
- fb();
- fc();
- return 0;
- }
-
-
- // 编译 gcc -C -S main.cpp -o main.txt
- .file "main.cpp"
- .section .rdata,"dr"
- __ZStL19piecewise_construct:
- .space 1
- .globl _A
- .data
- .align 4
- _A:
- .long 2
- .def ___main; .scl 2; .type 32; .endef
- .text
- .globl _main
- .def _main; .scl 2; .type 32; .endef
- _main:
- LFB935:
- .cfi_startproc
- pushl %ebp
- .cfi_def_cfa_offset 8
- .cfi_offset 5, -8
- movl %esp, %ebp
- .cfi_def_cfa_register 5
- andl $-16, %esp
- call ___main
- call __Z2fbv
- call __Z2fcv
- movl $0, %eax
- leave
- .cfi_restore 5
- .cfi_def_cfa 4, 4
- ret
- .cfi_endproc
- LFE935:
- .ident "GCC: (MinGW.org GCC-6.3.0-1) 6.3.0"
- .def __Z2fbv; .scl 2; .type 32; .endef
- .def __Z2fcv; .scl 2; .type 32; .endef
-
-
-
- =====================================================================================
- 从预编译和编译的过程我们看出,b.cpp / c.cpp / main.cpp 这三个文件的预编译和编译过程均没有问题
- 那么我们看一下,这个程序最后一步:链接过程
-
从预编译和编译的过程我们看出,b.cpp / c.cpp / main.cpp 这三个文件的预编译和编译过程均没有问题
那么我们看一下,这个程序最后一步:链接过程
1: 然后分别编译gcc -c b.cpp -o b.o和gcc -c main.cpp -o main.o

2: 然后链接 b.o 和main.o 文件 (gcc b.o main.o -o main,)就会出错,为什么了

到此,我们有了b.o和c.o,编译main.cpp后有了main.o,再将它们链接起来生成main时出现问题了:
4: 如何避免重复定义
讲了这么多,那么到底怎么样才能避免重复定义呢?
其实避免重复定义关键是要避免重复编译,防止头文件重复包含是有效避免重复编译的方法,但是最好的方法还是记住那句话:头文件尽量只有声明,不要有定义。这么做不仅仅可以减弱文件间的编译依存关系,减少编译带来的时间性能消耗,更重要的是可以防止重复定义现象的发生,防止程序崩溃。