C++在两个 .h头里未定义外部错误两次重复的类型 怎么解决?

1254人阅读
说明:出现这个错误,请你先检查重复定义的变量是否是定义在了.h头文件中,如果是,请您耐心的看完这篇文章,他会告诉你错误的根本原因。
如果你很着急,不想弄清楚原因,请直接按下面的方法更改:
假设重复定义的变量是int a,且你定义在了b.h,想作为全局变量使用,那么:
1.删除b.h中的int a
2.在b.cpp中加入a的定义
3.在b.h中加入
4.在要使用a的cpp文件中加入#include &b.h&
学习计算机那么久了,也编程这么久了,但是一直都没有彻底弄清楚头文件互相包含时,为什么有时候会出错,出现重复定义,有时候确又能够正常编译链接,今天仔细研究了一下这个问题,将结论记录下来。
编译,大家都知道,最后就会产生一个.o文件,我以前估计就理解到这个水平。于是我常常想不通一个问题,比如MFC的一个大工程,里面有a.cpp,a.h,b.cpp,b.h,那他们是合在一起编译吗?那互相包含会不会出错?
结论:编译是针对一个文件来说的,比如有a.cpp,a.h,b.cpp,b.h,他们之间的编译是没有关系的,a.cpp和a.h会产生一个a.o,他的编译和b完全没有关系,同样b.cpp和b.h产生的b.o和a也没有关系(除了include包含的情况)。
链接就很有可能是很多.o文件一起链接,组成一个可执行文件,例如g++ a.cpp b.cpp -o c,这样就是先单独编译了a.cpp和b.cpp并将产生的a.o,b.o链接为c
4.头文件包含的问题
我们都知道,ifndef是为了防止头文件重复包含,比如
#include &b.h&
#include &c.h&
#include &c.h&
这样的话,我们编译a.cpp肯定会出现问题,因为a.h里面包含了2次c.h,而c.h里面没有加入#ifndef之类的语句
所以我们需要将c.h改为下面的形式:
#ifndef _C_HEADER
#define _C_HEADER
这样的话,编译a.cpp就没错了,但是我们要注意,#ifndef只是针对某一个文件,而不是一个工程,比如你在a.h里面包含了c.h,在b.h里面也包含了c.h,这样不管你的c.h有没有加入#ifndef,你的c.h文件在a.h和b.h当中都会展开,而不是说a和b因为是一个工程,所以只展开一次。
5.extern的用法
首先,头文件.h中不适宜定义变量,我们都知道定义全局变量的常规使用方法是在.cpp文件中定义变量,在.h文件中用extern申明,这是为什么呢?看下面的例子:
比如在MFC的一个大工程中,我想定义一个全局变量int a,因为所有的文件都包含stdafx.h,我们考虑写在里面
第一种写法:
stdafx.h:
#include &stdafx.h&
#include &stdafx.h&
编译,没错耶!!!好开心
链接,怎么出错了?!!!重复定义a?好难过....
我就是这么难过....终于弄除了原因:
原因:在编译的时候,由于是一个一个文件为单位,所以a.cpp a.h被单独编译产生a.o,这样在编译的时候,由于他包含了stdafx.h,所以在a.o中,已经有int a的定义了
而b.cpp b.h也是被单独编译的,产生b.o里面也有一个int a的定义,所以出现了重复定义的错误。
正确的写法
stdafx.cpp
#include&stdafx.h&
#include&stdafx.h&
这样会发现编译链接都正确,这是为什么呢?
因为在编译的时候,由于mfc会自己默认编译stdafx.h和stdafx.cpp产生stdafx.o文件,然后我们的a.cpp编译的时候包括了stdafx.h,但是stdafx.h里面只有extern a,这就说明,我们在下面使用到的a都是在其他文件中定义的,同样的b.cpp编译时,也不会定义a,所以编译出来的a.o和b.o都不包含a的定义。最后链接,链接的时候,由于这是一个工程,所以mfc会把stdafx.o,a.o,b.o链接在一起,这样我们的a就定义了一次,在stdafx.o中。
看到这里,你可能会觉得奇怪,我都不知道我按下vc6.0的编译按钮,是编译了那么多文件,按下链接按钮,又是把所有文件链接到一起了,这些可能需要进一步了解makefile之类的东西。
1.编译是针对一个一个文件来说的,而链接则是针对一个工程所有的.o文件而言的。
2.#ifndef只是对防止一个文件的重复编译有效
3.全局变量最好在.cpp文件中定义,在.h文件中加上extern申明,因为在.h文件中定义,容易在链接时造成变量重定义。
4.注意mfc中会自动编译stdafx,并将stdafx.o加入到工程中一起链接.
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:210354次
积分:1879
积分:1879
排名:第8347名
原创:56篇
转载:18篇
评论:98条
(1)(1)(1)(1)(2)(1)(1)(2)(6)(1)(2)(3)(1)(10)(1)(4)(10)(1)(1)(2)(4)(1)(2)(2)(4)(2)(5)(3)(1)您现在的位置: &
C++基础:C++中的类型重定义
C++基础:C++中的类型重定义
  这几天工作时碰到一个C++的编译错误(我使用的是Visual C++ 7.0),说是有一个类重复定义,仔细想想我们的这个项目也是做了好几个Release了, 内部代码应该不会有这样的低级错误, 真把类型给重复定义了,检查结果正如我预料的一样。 就这样, 我左右没找到原因,被一个编译错误给卡在那里了。(在我的概念中, 程序错误的等级为:编译错误-&链接错误-&逻辑错误, 此错误属于最低级 )。这时我仔细看了一下错误提示, 发现重复定义是由于从两个不同的路径包含了同一个头文件而引起的,同事也建议从另外一个路径打开工程试试, 这才慢慢发现了原因。这个原因可能有些拗口,而事实上要出现这种错误也有些“曲折“, 让我从不同情况下的类型重定义来解释一下吧。&
  我总结的C++中类型重定义情况有三。&1 没有在文件头加#pragma once指示符。
  Type1.h:& #pragma once的作用是保证本文件只被编译一次,如果没有在Type1.h中加这句话,那么在main.cpp里面包含了两次Type1.h, 就相当于在main.cpp里面定义了两次Type类, 自然就是类型重定义了。
  //#pragma once
  class Type
  Main.cpp:
  #include \"Type1.h\"
  #include \"Type1.h\"
  int main(int argc, char *argv[])
  return 1;
  2 两个不同的头文件中定义了相同的类型(均有#pragma once)
  Type1.h:Type2.h:Main.cpp:
  #pragma once
  class Type
#pragma once
  class Type
  {&&&&&
  这里main.cpp中同时包含了Type1.h, Type2.h两个头文件, 虽然其文件头都有#pragma once,但因为是不同的文件, 预编译器还是会两次把Type类的定义放在Main.cpp中, 所以也会出现了重定义。
  #include \"Type1.h\"
  #include \"Type2.h\"
  int main(int argc, char *argv[])
  return 1;
  3 从两个不同的路径包含了同一个头文件
  前面两种是比较常见, 也是比较容易解决的情况, 而这里要讲的第三种情况, 比较少见, 而且一般出现在有虚拟映射盘的时候。(这样才能做到从两个不同的路径包含同一个头文件), 其他会在什么时候出现, 我还没想到, 知道的朋友顶一下:)。下面我来分析一下:&&&& 1) 有VC工程在D:\\Test目录下。&&&& 2) 映射虚拟盘X为D:\\Test. &&&& 不熟悉的网友可以按此操作: 开始-&运行-&在运行窗口输入:cmd-&在cmd窗口输入:&&&& Subst X: D:\\Test-&回车。&&&& 3) 该工程有文件Type1.h, main.cpp
  Type1.h:
  #pragma once
  class Type
  Main.cpp:
  main.cpp这样包含了两个头文件, 从本质上来讲, 它们都对应于物理盘D:\\Test下的文件Type1.h, 是同一个文件。但在不同的操作下, VC对其有不同的解释。#include \"X:\\Type1.h\"用的是绝对路径, 自然没有什么异议, 但#include \"Type1.h\"却有些变化:
  *假如我从D:\\Test\\下打开工程, 那么#include \"Type1.h\"其实就是#include \"D:\\Test\\Type1.h\"&
  *假如从X:\\下打开工程,那么#include \"Type1.h\"就解释为#include \"X:\\Type1.h\"&
  #include \"Type1.h\"
  #include \"X:Type1.h\"
  int main(int argc, char *argv[])
  return 1;
  这里我们在
  4) 在D:\\Test下打开工程, 编译, 出现类型Type重复定义错误 &&& &&& 这种情况下,main.cpp预编译为:
  Main.cpp:
  只保证本文件被编译一次, 这里VC将其认为是两个不同的文件, 所以都要编译, 出现编译错误自然也就不奇怪了。
  当然, 这里如果从X:\\ 下打开工程的话,VC就会认为都是从X:\\Type1.h下包含这个文件,#pragma once起到了作用, 也就不会出现类型重定义了
  #include \"D:TestType1.h\"
  #include \"X:Type1.h\"
  int main(int argc, char *argv[])
  return 1;
  #pragma once
  我在VC7, VC8,和Dev C++中都测试了第三种情况, 发现只有Dev C++是可以通过编译的。这可能是微软VC的#pragma once还不够智能吧,轻易的被Windows的虚拟盘给蒙蔽了双眼, 看不到其本质(只是猜测, 或许VC这么处理是有其他用意的)。&&&& 因为在稍大一点的工程开发中, 我们一般都会用虚拟盘来方便工作, 一是访问快捷,简化了路径, 二是因为多人协同开发,我们一般希望大家源代码路径相同,但我们不应强制要求大家都把源代码放死在某一目录下, 这时把你放源代码的路径映射为一个虚拟盘(比如说统一为X:)就能把大家的代码路径统一起来了。但是另一方面,有了虚拟盘, 就为出现类型重定义提供了条件, 以下是我得出的两个解决方法:
  1 抛弃#pragma once使用古老但集稳定性与移植性于一身的&&&& 来保证头文件只被编译一次。这样不管是包含两个相同的文件,还是包含两个不同的文件,或是包两个文件相同但路径不同的文件, 只要_XXX_H被定义过, 就不会再编译那个编译(但这里我们要保证_XXX_H的唯一性, 如果两个不同的头文件里用了同一_XXX_H,是会出问题的)
  #ifndef _XXX_H
  #define _XXX_H ...
  #endif &&& 2 在包含头文件时,不要使用绝对路径, 哪怕那是虚拟盘的绝对路径。
&&&主编推荐
&&&热门试卷
&&&最新视频
&&&热门阅读
&&&最新问答
&&&&&&&&&&&&&&&
希赛网 版权所有 & &&&&增值电信业务经营许可证湘B2-c++ 中头文件重复定义的问题
 一、同一头文件中类嵌套的疑问
  假设我们有两个类A和B,分别定义在各自的头文件A.h和B.h中,但是在A中要用到B,B中也要用到A,像下面的写法是错误的:
  class B;
  class A
  public:
  class B
  public:
  因为在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:
此页面上的内容需要较新版本的 Adobe Flash Player。
相关报道:
新闻热线:010-
责任编辑:
名企动态: |
但目前国内有实力超越“IOE”们的厂商委实不多……
本站特聘法律顾问:于国富律师
Copyright (C) 1997-当前位置: >
> 关于C++Builder6.0中引用C++头文件中的类创建对象的问题比如说:我在头文件a.h中定义
关于C++Builder6.0中引用C++头文件中的类创建对象的问题比如说:我在头文件a.h中定义
qzsong1983 & at
关于C++ Builder 6.0中引用C++头文件中的类创建对象的问题比如说:我在头文件a.h中定义了一个类 ManCom,然后再C++ Builder 6.0中新建了一个工程,在Uint1.cpp文件中引用了头文件,并且在窗体拉了一个Label、一个Edit组件、一个Button组件,现在在Uint1.cpp文件中中编写代码要用到类ManCom来创建一个ManCom类的对象mc,使得这个对象可以被Label、Edit、Button组件所操作(就是双击Button组件后,在里面编写代码,要可以使用对象mc,要在Uint1.cpp文件中哪个位置定义mc对象呢????麻烦知道的朋友尽快回复一下,谢谢了!
可以在Unit1.h中引用头文件a.h,然后在其中定义类Man_C的一个对象mc,这样在Unit1.cpp中就可以直接使用对象mc了!!!xichxu & &
& & (0)(0)
本问题标题:
本问题地址:
温馨提示:本问题已经关闭,不能解答。
暂无合适的专家
&&&&&&&&&&&&&&&
希赛网 版权所有 & &&&&增值电信业务经营许可证湘B2-}

我要回帖

更多关于 未定义外部错误 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信