[C++]头文件重复包含问题

本文详细解析了C++中头文件重复包含的问题及解决方案,包括使用#ifndef...#endif预处理指令和#pragma once的方式避免重复包含,并讨论了外部链接型变量导致的重定义问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文转自http://www.cnblogs.com/geore/p/5803944.html,主要记录了C++中头文件重复包含的问题。

关于头文件重复包含的问题,以前一直不太清楚,今天特意翻了一下参考书和网上查阅资料,有了如下的理解:

  这样说明,如果定义了头文件A.h,B.h和源文件C.cpp。如果我们在A.h中写上一个函数,在B.h中include A.h,然后再在C.cpp中include A.h和B.h,这样我们就会出现重复包含的问题,如下图:

下面看下问题代码:

 Multiple define error: 1

出错截图:

 

 *.出错分析:

  我们可以看到错误代码的提示:error C2084: function 'int sub(int,int)' already has a body,这说明sub函数已经存在了一个。这是什么原因,因为在B.h中我们通过#include "A.h"导入了A.h的头文件。而我们都知道头文件处理简单的来说就是将A.h中文件中的所有的东西全部的复制到#include <A.h>的定义所在的文件处。既然如此我们就可这样的理解,如下代码:

复制代码
 1 ////////////////////////////////////////////////////////////////////////////////////////////////////                   A.h  files
 2 
 3 //用于测试头文件的重复包含和重定义的问题
 4 #include <iostream>
 5 using namespace std;
 6 
 7 int sub(int a, int b)
 8 {
 9     std::cout << "sub: a-b = " << a - b << std::endl;
10     return a - b;
11 }
12 
13 int add(int a, int b)
14 {
15     std::cout << "sub: a+b = " << a + b << std::endl;
16 
17     return a + b;
18 }
19 
20 
21 ////////////////////////////////////////////////////////////////////////////////////////////////////                   B.h  files
22 
23 #include <iostream>
24 using namespace std;
25 
26 //#include "A.h"
27 //#include "A.h"就是相当于将A.h中的内容copy到了B.h中
28 
29 /////////////////////////////////////////////////////////////////////////////////////////////////////
30 //copy部分A.h中的函数,如下:
31 int sub(int a, int b)
32 {
33     std::cout << "sub: a-b = " << a - b << std::endl;
34     return a - b;
35 }
36 
37 int add(int a, int b)
38 {
39     std::cout << "sub: a+b = " << a + b << std::endl;
40 
41     return a + b;
42 }
43 ////////////////////////////////////////////////////////////////////////////////
44 
45 int mul(int a, int b)
46 {
47     std::cout << "sub: a*b = " << a * b << std::endl;
48 
49     return a * b;
50 }
51 
52 
53 
54 ////////////////////////////////////////////////////////////////////////////////////////////////                C.cpp files
55 
56 #include <iostream>
57 using namespace std;
58 
59 #include "A.h"
60 #include "B.h"
61 
62 //这里的A.h和B.h中的内容也会copy到C.cpp中来
63 
64 int main(void)
65 {
66 
67     //add(1, 2);
68     //sub(45, 45);
69 
70     return 0;
71 }
复制代码

  那么,这样看之后,我们就可以发现一个问题,在A.h中有两个函数sub()和add()定义;在B.h中同样的包含了两个函数sub()和add()定义,当A.h和B.h单独分开始这样还不会有问题。但是我们在看下C.cpp文件中有:#include "A.h" 和 #include "B.h"。这样的话又重复的将A.h和B.h文件中的内容复制到了C.cpp中,那么这样就出现问题了,在A.h和B.h中的相同函数全部复制到C.cpp中。那么在编译的时候编译器就会发现重定义了两个相同的函数,也就是上面的错误。编译上面Multiple define error: 2中的代码同样出现于Multiple define error: 1中的一样的错误。

 

  因此,总的来说,头文件重复包含的问题往往是重定义的问题。下面我们有两种方式解决头文件的重复包含:一个是条件编译的#ifndef...#endif 和 #pragma once.

如下代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//////////                      C.cpp files
 
#include <iostream>
using namespace std;
 
#include "A.h"
#include "B.h"
 
int main(void)
{
 
    add(1, 2);
    sub(45, 45);
    return 0;
}
 
 
///////////                      A.h files
 
//用于测试头文件的重复包含和重定义的问题
#ifndef _A_H_
#define _A_H_
 
#include <iostream>
using namespace std;
 
int sub(int a, int b)
{
    std::cout << "sub: a-b = " << a - b << std::endl;
    return a - b;
}
 
int add(int a, int b)
{
    std::cout << "sub: a+b = " << a + b << std::endl;
 
    return a + b;
}
 
#endif // _A_H_
 
 
/////////////                        B.h files
 
#ifndef _B_H_
#define _B_H_
 
#include <iostream>
using namespace std;
 
#include "A.h"
 
int mul(int a, int b)
{
    std::cout << "sub: a*b = " << a * b << std::endl;
 
    return a * b;
}
 
#endif // _B_H_

*.如上代码加入#ifndef...#endif就消除了重定义的问题了。其实这种方法消除的原理是在当编译器遇到第2(3,....)遍同样的头文件时,因为已经编译了一次,在后面在遇到的时候,编译器自动忽略文件中的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
///////////                          C.cpp
 
#include <iostream>
using namespace std;
 
#include "A.h"
#include "B.h"
 
int main(void)
{
 
    add(1, 2);
    sub(45, 45);
    return 0;
}
 
 
 
////////////////                      A.h
 
//用于测试头文件的重复包含和重定义的问题
//#ifndef _A_H_
//#define _A_H_
#pragma once
 
#include <iostream>
using namespace std;
 
int sub(int a, int b)
{
    std::cout << "sub: a-b = " << a - b << std::endl;
    return a - b;
}
 
int add(int a, int b)
{
    std::cout << "sub: a+b = " << a + b << std::endl;
 
    return a + b;
}
 
//#endif // _A_H_
 
 
/////////////////////              B.h
 
//#ifndef _B_H_
//#define _B_H_
#pragma once
 
#include <iostream>
using namespace std;
 
#include "A.h"
 
int mul(int a, int b)
{
    std::cout << "sub: a*b = " << a * b << std::endl;
 
    return a * b;
}
 
//#endif // _B_H_

*.#pragma once同样能消除头文件的重复包含的问题。见下运行实例图:

同样的上面的#ifndef...#endif 和 #pragma once 并不能完全消除所有的重定义问题。如下代码:

复制代码
 1 //////////////////////   A.h
 2 
 3 #ifndef _A_H_
 4 #define _A_H_
 5 
 6 int a = 10;
 7 
 8 #include <iostream>
 9 using namespace std;
10 
11 int sub(int a, int b)
12 {
13     std::cout << "sub: a-b = " << a - b << std::endl;
14     return a - b;
15 }
16 
17 int add(int a, int b)
18 {
19     std::cout << "sub: a+b = " << a + b << std::endl;
20 
21     return a + b;
22 }
23 
24 #endif // _A_H_
25 
26 
27 ////////////////////    B.h
28 
29 #ifndef _B_H_
30 #define _B_H_
31 
32 #include <iostream>
33 using namespace std;
34 //#include "A.h"
35 
36 int a = 5;
37 
38 int mul(int a, int b)
39 {
40     std::cout << "sub: a*b = " << a * b << std::endl;
41 
42     return a * b;
43 }
44 
45 #endif // _B_H_
复制代码

Multiple define error: 3出现如下错误:

  看上面的代码,我们已将a变量的声明放在了条件编译之中,但是还是出现了重定义的问题,这是什么原因呢?在C++静态变量中有三种类型,其中有一种外部链接型的静态变量。而全局变量在声明的时候没有加上修饰词static,因此这种变量是属于外部链接型的变量。而外部链接的变量对于在同一个工程文件的所有文件中都是可见的,所以在其中一个文件中定义的外部链接变量a,同时在另一个文件中也定义。或者在包含头文件的时候重复包含了某一个定义了外部变量的头文件,就会导致在同一个工程文件形成的程序中将包含两个一样的变量(同样函数定义(Multiple define error:2)也是一样会产生同样的错误)。

  如何解决呢?解决的方法就是在a变量前加上extern修饰符即可;如下:

1
extern int a = 10;

  因此,在头文件的定义中,我们最好不要将函数定义和变量的声明放到头文件中。但是下面的几种确实头文件常包含的内容:

1
2
3
4
5
6
函数声明
使用#define或者const定义的符号常量
类声明
结构体声明
模板声明
内联函数

如下: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#ifndef _B_H_
#define _B_H_
 
#include <iostream>
using namespace std;
 
#include "A.h"
 
#define pi 3.1415
 
const int value = 100;
 
int add(int a, int b);
 
class bb
{
    //......
};
 
struct bbb
{
    //......
};
 
inline void bbbb(int a, int b)
{
    cout << "a+b = " << a+b << endl;
}
 
int mul(int a, int b);
//{
//  std::cout << "sub: a*b = " << a * b << std::endl;
//
//  return a * b;
//}
 
#endif // _B_H_

  其中函数声明不用说都知道,类声明和结构体声明在头文件中不会导致重定义是因为类和结构体中的不会创建变量只是在源文件中声明结构体变量的时候。因此不会出现重定义问题。内联函数和模板的声明这两个我还没有查到具体的原因(待续)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值