C++ 'include' 的两个头文件互相包含出错

头文件保护符

通常,现在写代码时都会随手在头文件中加入头文件保护符。有以下两种形式的保护符。

 
  1. #ifndef ABCDE

  2. #define ABCDE

  3. //..... 内容

  4.  
  5. #endif

#pragma once


两种模式的作用是相同的,都是只让头文件在解析过程中只会展开一次。

能工作的循环包含情况

今天遇到的问题是循环包含问题,也就是说如下例子

 
  1. //********************************* a.h 文件

  2.  
  3. #pragma once

  4. #include <iostream>

  5. #include "b.h"

  6. using namespace std;

  7.  
  8. int a = 1;

  9.  
  10. //********************************* b.h文件

  11.  
  12. #pragma once

  13. #include "a.h"

  14.  
  15. int b = 1;



这是虽然有循环包含,但是由于有头文件保护符的作用,则两者之间是不冲突的,假设存在以下cpp文件:

 
  1. #include "a.h"

  2. #include "b.h"

  3.  
  4. int main(){

  5. cout << a << endl;

  6. cout << b << endl;

  7. }


根据 cpp 文件中包含的头文件的顺序,则该cpp 文件会被展开为如下情况:

 
  1. //-----------------------------------#include "a.h"

  2.  
  3. #include <iostream>

  4. //----------------------#include "b.h"

  5. int b = 1;

  6. //----------------------

  7. using namespace std;

  8.  
  9. int a = 1;

  10.  
  11. //------------------------------------

  12.  
  13. int main(){

  14. cout << a << endl;

  15. cout << b << endl;

  16. }


 

可以看出,该代码的使用完全没有问题、

出错的循环包含情况

但是如果出现以下这种情况,则循环包含会出错,且报错都是编译解析错误,即分号错误,<号错误等毫无问题的错误。

 
  1. //**************************************** a.h 文件

  2.  
  3. #pragma once

  4. #include "b.h";

  5. #include <iostream>

  6. using namespace std;

  7.  
  8. class A {

  9. };

  10.  
  11.  
  12. //*************************************** b.h 文件

  13.  
  14. #pragma once

  15. #include "a.h"

  16.  
  17. class B {

  18. A * aptr;

  19. };


 

 cpp文件如下:

 
  1. #include "a.h"

  2. #include "b.h"

  3.  
  4. int main() {

  5. }


 

此时报错就比较迷离了。会出现错误如下:

错误 C2143语法错误: 缺少“;”(在“*”的前面)Project2d:\文档\visual studio 2015\projects\project2\project2\b.h5

错误 C2238意外的标记位于“;”之前Project2d:\文档\visual studio 2015\projects\project2\project2\b.h5

分析时尝试将两个头文件展开在cpp文件中,展开结果如下

 
  1. //------------------------#include "a.h"

  2.  
  3. //-----------#include "b.h"

  4.  
  5. class B{

  6. A * aptr;

  7. };

  8.  
  9. //------------

  10.  
  11. #include <iostream>

  12. using namespace std;

  13.  
  14. class A{

  15. };

  16.  
  17. //---------------------------

  18.  
  19. int main(){

  20. }

此时可以明显看出,问题出现在,当将头文件按其各自在cpp中声明的顺序展开时,会出现在 class B 中,类型 A 未经过声明就使用了的情况。

这个问题难在三个点上:

1. 编译器报错的地方很奇怪,说是分号错误类似的问题,根本无从定位

2. 这个问题在包含顺序变动后很可能就消失了,但是弊端还是存在的。假设上例中 cpp 文件先包含 b.h 再包含 a.h ,则就没有问题

3. 若是不止两个头文件间发生了循环包含,而是多个头文件循环包含,则问题更难定位。如 a 包含 b,b 包含 c,c 包含 a,则比较隐晦。

反思

说明:通常在面向对象编程中,我们在头文件中都是定义类,若两个头文件包含,则说明两个头文件中包含的两个类之间关系高度耦合。即A 需要 B , B 需要 A。依照《重构》中的思想,这种情况属于代码臭味,需要考虑将这两个类中的内容合并为一个类,之后将不存在循环包含的情况。类之间通常是单向依赖关系,双向依赖关系意味着臭味,需要对代码进行拆分重组,因此当出现循环包含时,应当考虑代码重构。 

猜你喜欢

转载自blog.csdn.net/qq1841370452/article/details/81321681