C++中头文件、源文件之间的区别与联系

若如初见. 提交于 2019-11-30 17:17:02
.h头文件和.cpp文件的区别
疑惑1:.h文件能够编写main函数吗?
实验:
编写test.h文件,里面包含main函数
若直接编译g++ test.h -o test,通过file命令 file test,得到如下结果test: GCC precompiled header (version 013) for C++ ———test文件是预编译头文件

推测:.h文件不能单独生成.o文件

疑惑2:.h文件中声明和定义的变量和函数是如何与.cpp文件结合的?
实验:
编写test.h文件,里面包含一个变量定义以及函数定义,编写test.cpp文件包含该头文件,通过g++ -E test.cpp -o test.i生成预编译文件,打开test.i文件发现,上面包含了头文件中变量的定义以及函数的定义,就像平时我们可以不写.h文件,只写.cpp文件一样,把所有的声明和定义都放在了一个.cpp文件中。

test.h

#ifndef _TEST_H
#define _TEST_H
int var = 1;
void func {

}
#endif
test2.h
#ifndef _TEST2_H
#define _TEST2_H
#include "test.h"
#endif
test.cpp
#include "test.h"
#include "test2.h"

int main ()
{
    var = 2;
    return 0;
}

gcc -E test.cpp -o test.i 得到test.i预编译文件

# 1 "test.cpp"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "test.cpp"
# 1 "test.h" 1


int var = 1;
void func {

}
# 2 "test.cpp" 2
# 1 "test2.h" 1
# 3 "test.cpp" 2

int main () 
{
    var = 2;
    return 0;
}

推测:.cpp文件中的#include预编译指令将.h的文件内容包含进来(#include指令是递归执行的),并通过条件预编译指令#ifndef、#define、#endif将重复的.h头文件去除,否则会在编译过程中出现重定义错误。

一些思考:
1、.h头文件的作用只出现在预编译阶段,预编译后.h文件就失去了价值,也就是说一个.cpp文件为一个编译单元,一个.cpp文件最终生成一个.o文件
2、.h头文件应该包含些什么?
a. 包含声明、条件预编译指令以及类的定义(类的定义一定要有条件预编译指令,否则多个文件包含会出现重定义错误)
b. 包含需要的的头文件,无关的头文件都不需要包含,cpp使用的头文件都放入cpp文件中,或者单独建立一个总体的头文件包含所有需要使用的头文件
c. 包含常量(包括const常量声明,定义放在源文件中)和宏定义


在头文件中定义变量或函数会出现什么问题?(全局变量和函数默认是extern属性)

local_comm.h

#ifndef _LOCAL_H
#define _LOCAL_H
int var = 2;
void func() {};
#endif

test1.cpp

#include <stdio.h>
#include "local_comm.h"

void func1() 
{
        printf("func1 &var=%lu\n", &var);  
}
test2.cpp
#include <stdio.h>
#include "local_comm.h"

void func2() 
{
        printf("func2 &var=%lu\n", &var);  
}
main.cpp
extern void func1();
extern void func2();
int main()
{
        func1();
        func2();
}
g++ main.cpp test1.cpp test2.cpp 编译报错
$g++ main.cpp test1.cpp test2.cpp 
/tmp/ccGkEzQE.o: In function `func()':
test2.cpp:(.text+0x0): multiple definition of `func()'
/tmp/ccpTecbJ.o:test1.cpp:(.text+0x0): first defined here
/tmp/ccGkEzQE.o:(.data+0x0): multiple definition of `var'
/tmp/ccpTecbJ.o:(.data+0x0): first defined here
collect2: ld returned 1 exit status
结论:头文件的条件预编译指令只能去掉同一个编译单元中包含的重复定义,不能在链接的时候去掉各个编译单元中的相同的定义,因为普通变量和函数默认属性是全局的,也就是在整个最后生成的exe中只能有唯一一个同名的变量和函数。


在头文件中定义static静态变量或者静态函数会出现什么问题?(全局const变量默认是static属性)

修改local_comm.h,将变量和函数改成static属性。

#ifndef _LOCAL_H
#define _LOCAL_H
static int var = 2;
static void func() {};
#endif

编译运行:

$g++ main.cpp test1.cpp test2.cpp
$./a.out
func1 &var=4196096
func2 &var=4196116
结论:静态变量和静态函数的作用域只是在本编译单元(.o文件),在不同的副本都保存了该静态变量和静态函数,正是因为static有以上的特性,所以一般定义static全局变量时,都把它放在原文件中而不是头文件,这样就不会给其他模块造成不必要的信息污染。





易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!