`
yiyeqinghuasoon
  • 浏览: 643388 次
文章分类
社区版块
存档分类
最新评论

c++ 中头文件重复定义的问题

 
阅读更多

一、同一头文件中类嵌套的疑问

假设我们有两个类A和B,分别定义在各自的头文件A.h和B.h中,但是在A中要用到B,B中也要用到A,像下面的写法是错误的:

class B;

class A

{

public:

B b;

};

class B

{

public:

A a;

};

因为在A对象中要开辟一块属于B的空间,而B中又有A的空间,是一个逻辑错误,无法实现的。在这里我们只需要把其中的一个A类中的B类型成员改成指针形式 就可以避免这个无限延伸的怪圈了。为什么要更改A而不是B?因为就算你在B中做了类似的动作,也仍然会编译错误,表面上这仅仅上一个先后顺序的问题。

为什么会这样呢?因为C++编译器自上而下编译源文件的时候,对每一个数据的定义,总是需要知道定义的数据的类型的大小。在预先声明语句class B;之后,编译器已经知道B是一个类,但是其中的数据却是未知的,因此B类型的大小也不知道。这样就造成了编译失败,VC++6.0下会得到如下编译错 误:

error C2079: 'b' uses undefined class 'B'

将A中的b更改为B指针类型之后,由于在特定的平台上,指针所占的空间是一定的(在Win32平台上是4字节),这样可以通过编译。

二、不同头文件中的类的嵌套在实际编程中,不同的类一般是放在不同的相互独立的头文件中的,这样两个类在相互引用时又会有不一样的问题。重复编译是问题出现的根本原因。为了保证头文 件仅被编译一次,在C++中常用的办法是使用条件编译命令。

Example:

animal.h

class animal

{

......

};

animal.cpp

#include "animal.h"

#include <iostream.h>

......

fish.h

#include "animal.h"

class fish

{

......

};

fish.cpp

#include "fish.h"

#include <iostream.h>

......

main.cpp

#include "animal.h"

#include "fish.h"

void main()

{

......

}
编译文件,会出现class type redefinition的错误

为什么会出现类重复定义的错误呢?请读者仔细查看EX10.cpp文件,在这个文件中包含了animal.h和fish.h这两个头文件。当编译器编译 EX10.cpp文件时,因为在文件中包含了animal.h头文件,编译器展开这个头文件,知道animal这个类定义了,接着展开 fish.h 头文件,而在fish.h头文件中也包含了animal.h,再次展开animal.h,于是animal这个类就重复定义了。

要解决头文件重复包含的问题,可以使用条件预处理指令。

修改后的头文件如下:

animal.h

#ifndef ANIMAL_H_H

#define ANIMAL_H_H

class animal

{

......

};

#endif

fish.h

#include "animal.h"

#ifndef FISH_H_H

#define FISH_H_H

class fish

{

......

};

#endif

我们再看EX10.cpp的编译过程。当编译器展开animal.h头文件时,条件预处理指令判断ANIMAL_H_H没有定义,于是就定义它,然后继续 执行,定义了animal这个类;接着展开fish.h头文件,而在fish.h头文件中也包含了animal.h,再次展开 animal.h,这个时候条件预处理指令发现ANIMAL_H_H已经定义,于是跳转到#endif,执行结束。

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

//文件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;

class A

{

public:

B* b;

};

//文件B.h中的代码

#pragma once

#include "A.h"

class A;

class B

{

public:

A* a;

};

这样至少可以说明,头文件包含代替不了前置声明。有的时候只能依靠前置声明来解决问题。我们还要思考一下,有了前置声明的时候头文件包含还是必要的 吗?我们尝试去掉A.h和B.h中的#include行,发现没有出现新的错误。那么究竟什么时候需要前置声明,什么时候需要头文件包含呢?

三、两点原则

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

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

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

分享到:
评论

相关推荐

    C/C++语言中全局变量重复定义问题的解决方法

    在C语言中使用extern 关键字来定义全局变量的时候,我们需要在.h文件和.c文件中重复定义,这种重复,导致了出错几率的增加。 今天,在整理自己的代码的时候,考虑到我写的代码从一至终都是在一个cpp文件里面。于是,...

    C++经典语法掌握C++

    VC工程的编译原理与过程,将工程中不同的类拆分到不同的原文件中,每一个类由一个.h和.cpp文件共同完成,头文件重复定义问题的解决,培养了学员良好的编程习惯,也为以后分析MFC Appwizard生成的工程奠定了良好基础...

    C++经典语法与应用实例源码

    摘要:VC/C++源码,其它分类,Appwizard  C++经典语法与应用,... 每一个类由一个.h和.cpp文件共同完成,头文件重复定义问题的解决,培养了学员良好的编程习惯,  也为以后分析MFC Appwizard生成的工程奠定了良好基矗

    C++经典语法与应用

    VC工程的编译原理与过程,将工程中不同的类拆分到不同的文件中,每一个类由一个.h和.cpp文件共同完成,头文件重复定义问题的解决,培养了学员良好的编程习惯,也为以后分析MFC AppWizard生成的工程奠定了良好基础。

    enum类型重定义解决方法

    常见的enum类型重定义的解决方法,清晰明了。

    解决C++中重定义的方法总结

    C++由于头文件重复包含了所定义的变量或者常量,编译器就会报重复定义的错误。如果你碰见这样的问题可以考虑重下面几个方面去解决: 1、在出现重定义错误的头文件加上:#ifndef FileName_H_#define FileName_H_ …....

    掌握C++的具体语法

    VC工程的编译原理与过程,将工程中不同的类拆分到不同的文件中,每一个类由一个.h和.cpp文件共同完成,头文件重复定义问题的解决,培养了学员良好的编程习惯,也为以后分析MFC AppWizard生成的工程奠定了良好基础。

    C++经典语法与应用,类的编写与应用

    VC工程的编译原理与过程,将工程中不同的类拆分到不同的文件中,每一个类由一个.h和.cpp文件共同完成,头文件重复定义问题的解决,培养了学员良好的编程习惯,也为以后分析MFC AppWizard生成的工程奠定了良好基础。

    c++经典语法

    VC工程的编译原理与过程,将工程中不同的类拆分到不同的原文件中,每一个类由一个.h和.cpp文件共同完成,头文件重复定义问题的解决,培养了学员良好的编程习惯,也为以后分析MFC Appwizard生成的工程奠定了良好基础...

    基于C++类型重定义的使用详解

    这几天工作时碰到一个C++的编译错误(我使用的是Visual C++ 7.0),说是有一个类重复定义,仔细想想我们的这个项目也是做了好几个Release了, 内部代码应该不会有这样的低级错误, 真把类型给重复定义了,检查结果正如...

    C++语言基础教程吕凤煮著

    宏定义的目的是防止头文件被重复引用,当编译器编译头文件时,判断当前宏是否被定义,若没定义则进行定义,并编译头文件,否则略过当前头文件 a、引用头文件 使用格式引用系统的头文件,例如#include 使用""格式...

    高质量c/c++编程设计

    C++/C程序的头文件以“.h”为后缀,C程序的定义文件以“.c”为后缀,C++程序的定义文件通常以“.cpp”为后缀(也有一些系统以“.cc”或“.cxx”为后缀)。 【规则1-2-1】为了防止头文件被重复引用,应当用ifndef/...

    C/C++ 中extern关键字详解

    推荐:在.h中声明,因为在头文件定义的话,其他模块include此头文件,就会报重复定义错误 实验结论 1、在.h中声明 extern int g_a; 在.c中定义 int g_a=1; 在两个其他文件中引入.h g_a就是唯一的 2、在.h中声明 ...

    gsoap 2.8 (SOAP/XML 关于C/C++ 语言的自动化实现工具内附 CSharp webservice例子,及GSOAP client和server例子)

    该预编译器的输入参数是一个标准的C/C++头文件  。这个头文件可以由WSDL解析器根据相关的WSDL文档自动生成。  参见下面的命令:  $ wsdl2h -o quote.h  上面的命令根据制定URL提供的WSDL文档生成一个C++语法结构...

    林锐--高质量C++-C编程指南

    C++/C程序的头文件以“.h”为后缀,C程序的定义文件以“.c”为后缀,C++程序的定义文件通常以“.cpp”为后缀(也有一些系统以“.cc”或“.cxx”为后缀)。 【规则1-2-1】为了防止头文件被重复引用,应当用ifndef/...

    C++经典语法以及类的编写与应用实例

    VC工程的编译原理与过程,将工程中不同的类拆分到不同的原文件中,每一个类由一个.h和.cpp文件共同完成,头文件重复定义问题的解决,培养了学员良好的编程习惯,也为以后分析MFC Appwizard生成的工程奠定了良好基矗

Global site tag (gtag.js) - Google Analytics