#ifndef 头文件卫士 只防止一个.cpp里的重定义(因为#define只作用于一个.cpp),而不是多个.cpp

转自:http://blog.csdn.net/tmljs1988/article/details/6045757

#ifndef 头文件卫士是为了保证类的头文件在一个.cpp文件中被多次引用后会不会出现重复定义的问题,注意,只是防止在一个.cpp文件中被多次引用.

 

 

#ifndef ELEMTYPE_H

#define ELEMTYPE_H

的意思是,如果前面没有定义ELEMTYPE_H,那么现在定义ELEMTYPE_H

它不需要有值,只是表明是否被定义过,它是为了防止头文件的重复定义

 

我举个简单的例子帮助你理解:

 

你在a.h里定义了一个类a:

 

class a{

 

}

 

然后在b.h里定义了一个类b,而且引用了a.h:

 

#include "a.h"

 

class b{

 

}

 

编译器在编译时,b.h的实际内容为:

 

class a{

 

}

 

class b{

 

}

 

这里还没出现错误。

 

 

然后你在c.cpp里引用了a.h和b.h:

 

#include "a.h"

 

#include "b.h"

 

那么,编译器在编译的时候会把a.h和b.h里的内容原封不动的填进来以代替这两句,这样c.cpp就变为:

 

class a{

 

}

 

class a{

 

}

 

class b{

 

}

 

你可以很清楚的看到,类a重定义了。

 

那么,如果我们用到前面提到的宏定义来分别定义a.h和b.h如下:

 

//a.h

 

#ifndef A_H

 

class a{

 

}

 

#define A_H

 

 

//b.h

 

#ifndef B_H

 

class b{

 

}

 

#define B_H

 

 

那么,c.cpp展开后为:

 

#ifndef A_H

 

class a{

 

}

 

#define A_H

 

 

 

#ifndef B_H

 

  #ifndef A_H

 

    class a{

 

    }

 

  #define A_H

 

  class b{

 

  }

 

#define B_H

 

 

可以看到,执行第一次class a的定义时,由于宏A_H未定义,故此时执行类a的定义,并定义A_H,然后执行

 

第二次class a的定义时,发现A_H已经定义,因此跳过a的再次定义,避免了重定义.

 

-------------------------------------------------------------------------------

在#ifndef中定义变量出现的问题(一般不定义在#ifndef中)。

如AAA.h

#ifndef AAA_H

#define AAA_H

 

int i; 

 

#endif

 

里面有一个变量定义在vc中链接时就出现了i重复定义的错误。

 

原因:

当你第一个使用AAA.h头的.cpp文件生成.obj的时候,int i 在里面定义了.当另外一个使用AAA.h的.cpp再次[单独]生成.obj的时候,int i 又被定义.然后两个obj连接在一起,就会出现重复定义.

特别注意:.cpp文件中的代码#include 某个.h文件相当于是.h文件复制到该.cpp文件中,且不同的.cpp文件引用同一个带ifndef的头文件时,不会冲突,即都会执行.如上面,如果a.cppb.cpp都引用了AAA.h,a.cpp中第一次会define AAA_H.但因为b.cpp是独立于a.cpp,所以AAA_Hb.cpp来说也未定义,所以它仍然会定义int i..

对于正常的类(C.h声明类C,C.cpp实现类C,main.cpp调用类C):

(1)C.h:

#ifndef HEADER_C

#define HEADER_C

class C

{

public:

    int Fn();

};

#endif

(2)C.cpp:

#include "C.h"

int C::Fn()

{

    return 1; 

}

(3) main.cpp:

#include "CA.h"

Main()

{

    C c;

    cout << c.Fn()<< endl;

}

 

上例中, C.cpp变为:

#ifndef HEADER_C//会定义class C

#define HEADER_C

class C

{

public:

    int Fn();

};

#endif

会定义class C(定义一个类的类型)

 

main.cpp也同样会定义

#ifndef HEADER_C //main.cpp中的HEADER_CC.cpp中的HEADER_C是独立的,不相干的,单独编译的,所以下面的class C依然会定义

#define HEADER_C

class C

{

public:

    int Fn();

};

#endif

也会定义class C,但编译通过,因为在多个.cpp中类型定义(class C)可以重复(但一个.cpp内不行,这就是头文件卫士的作用),详见附录.

 

推荐解决方案:.h中只声明 extern int i;

AAA.h:

 

#ifndef __X_H__

#define __X_H__

 

extern int i;

 

#endif

 

注意问题:变量一般不要在.h文件中定义。

 

 

附录:

类,其实是数据类型的定义,和structenum的定义没有区别,

1、类型定义和对象定义是有区别的:

类型定义的作用范围和enum的定义是一样,类似const对象的默认作用域,文件内部作用域,所以即使被多个.cpp文件包含进去也不会发生类类型重定义(每个.cpp文件都可有一个该类的定义,就像const,enum#define的值一样),除非被一个c文件多次展开,多次展开的情况才用#ifndef _H_  #define _H_头文件哨兵避免,但是非const对象定义就不同了,默认不是extern作用域,所以被多个c文件包含就会发生对象重定义,用不用头文件哨兵都一样(头文件哨兵只对某个.cpp有作用,对多个.cpp无用,因为每个.cpp是单独的,#define的作用域只是单个.cpp,而不是全局所有的.cpp)

 

2class A 

  .... 

}; 

是类型定义,也同时进行了一个类型声明,所以在很多书上直接说声明一个类,稍微不严谨;

class A;才是单纯类型声明,声明只是说明一个名字在此处用到,所以可以重复声明。

 

-----------------------------------------------------------------

()实验(2.cpp):

#pragma message()

 

//文件A

//test.cpp

 

#ifdef TEST

 

    #pragma message("TEST was defined! Found in test.cpp!")

 

#endif

--------------------------

//文件B

//main.cpp

 

#define TEST

 

#ifdef TEST

 

    #pragma message("TEST was defined!Found in main.cpp")

 

#endif

 

int main(void){return 0;}

--------------------------

编译过程中显示:

Compiling...

main.cpp

TEST was defined!Found in main.cpp

test.cpp

Linking...

 

也就是说你在B文件中#define的变量不会自动在A中被定义!

-----------------------------------------------------------------

()实验(有头文件.h,2.cpp):

(1)a.h:

#ifndef HEADER_A

#define HEADER_A

 

#endif

--------------

(2)a.cpp:

#include "a.h"

--------------

(3)main.cpp:

#ifndef HEADER_A

       cout <<"not defined";//此处亮,会执行,表明没有定义HEADER_A

#else

       cout <<"efined";//变灰,不会执行

#endif

 

#include "a.h"//包含a.h

 

#ifndef HEADER_A

       cout <<"not defined";//变灰,不会执行

#else

       cout <<"defined";//此处亮,会执行,包含a.h后定义了HEADER_A

#endif

 

这个程序也能说明:二个.cpp里的#define的变量不是全局的,而是只作用于当前.cpp.

 

所以得出结论:#ifndef HEADER_A  #define HEADER_A  #endif 头文件卫士是为了防止一个.cpp文件多次引用一个.h文件时产生重定义的错误,而不是多个.cpp!!

 

 

关于类的声明和定义。

    class A;  //类的声明(前向声明)

    类的声明和普通变量声明一样,不产生目标代码,可以在同一,以及多个编译单元重复声明。

   class A {

   };  //类的定义

   类的定义就特殊一点了,可能会有疑问,为什么不能把int x;这样的变量定义放到.h,但是可以把 类的定义放在头文件中重复引用呢?同时类的函数非inline定义(写在类定义里面的函数是inline,除外)不能写在头文件中呢。

  这是因为类的定义,只是告诉编译器,类的数据格式是如何的,实例话后对象该占多大空间。  类的定义也不产生目标代码。因此它和普通变量的声明唯一的区别是不能在同一编译单元内出现多次。

  //source1.cc

   class A;

   class A;  //类重复声明,OK

   class A{

   };

   class A{

   };       

   class A{

       int x;

  };    //同一编译单元内,类重复定义,会编译时报错,因为编译器不知道在该编译单元,A a;的话要生产怎样的a.

         //如果class A{};定义在head.h ,而head.h 没有 

         //#ifndef  #endif 就很可能在同一编译单元出现类重复定义的编译错误情况。

 

但是在不同编译单元内,类可以重复定义,因为类的定义未产生实际代码。

  //source1.cc

  class A{

  }

  //source2.cc

  class A{

  }                      //不同编译单元,类重复定义,OK。所以类的定义可以写在头文件中!

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值