c++头文件包含 #ifndef ##pragma once

时间:2023-01-12 16:18:27

2013-04-14 17:03 (分类:计算机程序)

烦死了,这种垃圾小问题很多,你又必须要知道。。。。。。。在编写c++程序时,会编写多个类或者多个cpp文件,免不了要多次使用include包含头文件,于是重复包含的问题就来了,怎样避免这一情况呢?

在此我们使用条件编译来解决这个问题

#ifndef x //先测试x是否被宏定义过
#define x

程序段1     //如果x没有被宏定义过,定义x,并编译程序段1

#endif

程序段2     //如果x已经定义过了则编译程序段2的语句,“忽视”程序段1。

条件指示符#ifndef 的最主要目的是防止头文件的重复包含和编译,#ifndef 和 #endif 要一起使用,如果丢失#endif,可能会报错。

一般格式是这样的:
#ifndef <标识>
#define <标识>
............
#endif<标识>
............

在理论上来说可以是*命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前面加下划线,并把文件名中的“.”也变成下划线,
如:stdio.h
#ifndef _STDIO_H_
#define _STDIO_H_
......
#endif

在高版本的VC++上,还可以使用这个命令来代替以上的所有:   #pragma once      它的意思是,本文件内的代码只被使用一次。
       Visual C++使用#pragma once来通知编译器在生成时只包含(打开)一次,也就是说,在第一次#include之后,编译器重新生成时不会再对这些包含文件进行包含(打开)和读取,因此我们看到在用向导创建的所有类的头文件中有#pragma once语句就不会觉得奇怪了。然而正是由于这个语句而造成了在第二次#include后编译器无法正确识别所引用的类。

不要以为使用了这种机制就全部搞定了,比如在以下的代码中:

//文件A.h中的代码
#pragma once

#include "B.h"

class A{      
public:       
   B* b;
         };

//文件B.h中的代码
#pragma once

#include "A.h"

class B{      
public:       
   A* a;
       };

这里两者都使用了指针成员,因此嵌套本身不会有什么问题,在主函数前面使用#include "A.h"之后,主要编译错误如下:      error C2501: 'A' : missing storage-class or type specifiers仍然是类型不能找到的错误。其实这里仍然需要前置声明。分别添加前置声明之后,可以成功编译了。代码形式如下:

//文件A.h中的代码
#pragma once

#include "B.h"

class B;       //前置声明类B

class A{      
public:    
      B* b;
};

//文件B.h中的代码
#pragma once

#include "A.h"

class B;       //前置声明类B

class B{    
  public:     
     A* a;
};

头文件包含其实是一想很烦琐的工作,不但我们看着累,编译器编译的时候也很累,再加上头文件中常常出现的宏定义。感觉各种宏定义的展开是非常耗时间的,远不如自定义函数来得速度。我仅就不同头文件、源文件间的句则结构问题提出两点原则,仅供参考:

第一个原则应该是,如果可以不包含头文件,那就不要包含了。这时候前置声明可以解决问题。如果使用的仅仅是一个类的指针,没有使用这个类的具体对象(非指针),也没有访问到类的具体成员,那么前置声明就可以了。因为指针这一数据类型的大小是特定的,编译器可以获知。

第二个原则应该是,尽量在CPP文件中包含头文件,而非在头文件中。假设类A的一个成员是是一个指向类B的指针,在类A的头文件中使用了类B的前置声明并编译成功,那么在A的实现中我们需要访问B的具体成员,因此需要包含头文件,那么我们应该在类A的实现部分(CPP文件)包含类B的头文件而非声明部分(H文件)。