您的位置:首页 > 编程语言 > C语言/C++

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

2010-11-30 16:23 330 查看
#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.cpp和b.cpp都引用了AAA.h,则a.cpp中第一次会define AAA_H.但因为b.cpp是独立于a.cpp的,所以AAA_H对b.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_C与C.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文件中定义。

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

2、class 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。所以类的定义可以写在头文件中!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐