因此,无论我做什么,我似乎都无法避免由于我在同一项目中的多个源代码文件中包含相同的头文件而使Dev C++发出大量多重定义错误.我非常希望避免将我的所有源代码转储到一个文件中,并且只包含一次标题,因为这将使我的文件很长并且难以管理.
从本质上讲,这是正在发生的事情:
#ifndef _myheader_h #define _myheader_h typedef struct MYSTRUCT{ int blah; int blah2; } MYSTRUCT; MYSTRUCT Job_Grunt; MYSTRUCT *Grunt = &Job_Grunt; MYSTRUCT Job_Uruk; MYSTRUCT *Uruk = &Job_Grunt; int Other_data[100]; void load_jobs(); #endif
示例Cpp文件(它们几乎都看起来像这样):
#include "myheader.h" void load_jobs(){ Grunt->blah = 1; Grunt->blah2 = 14; Uruk->blah = 2; Uruk->blah2 = 15; return; }
请记住,我有大约5个包含这个标题的cpp文件,每个文件都处理头文件中找到的不同类型的结构.在这个例子中,当实际头文件中有大约4-6个不同的结构,并且有更多的成员时,只有一个结构包含几个成员.我在其中包含的所有文件都遵循您在此示例中看到的相同公式.
现在我明白了标头防护只能阻止每个单独的cpp文件多次包含头文件.似乎正在发生的事情是,当编译器在每个cpp的开头读取include时,它会再次定义头文件,这会导致它吐出以下行和行:
Multiple Definition of Uruk, first defined here Multiple Definition of Job_Uruk, first defined here Multiple Definition of Grunt, first defined here Multiple Definition of Job_Grunt, first defined here Multiple Definition of Other_data, first defined here
我会在项目中看到一组包含标题的cpp文件.我已经尝试将struct和struct变量的定义移动到cpp文件,但是其他cpp文件无法看到它们或使用它们,这非常重要,因为我需要项目中的所有文件才能工作有了这些结构.
但是关于这个问题的最令人困惑的部分需要更多解释:
我在这个项目中设置这些多个文件的方式与我正在使用的书,John S. Harbour的All In One Game Programming相同.当我在书中创建示例项目的文件时,我遇到了完全相同的问题,这些项目要求在同一个项目中包含多个cpps的一个头文件.
我可以从书中逐字逐句地输入它们,而且我的确逐字逐句......
我会在项目中为每个cpp获得一系列MD错误.
如果我从本书附带的CD加载示例项目,它将编译并运行没有问题,尽管文件本身以及项目选项的外观与我创建的相同.
如果我创建了自己的项目文件,并且只是从CD中添加了示例项目的源文件和头文件,那么它也可以编译运行,但我发现这些文件和我的文件没有区别.
那么,我尝试制作我自己的项目文件,然后创建空白的源文件和头文件并将其添加到文件中,然后通过复制并粘贴它们要与之对应的CD上的文件来填充它们(相同)那些曾经工作的人).当然,我会得到同样的东西...... MD错误消息的行和行.
我非常困惑.我已经多次重复所有这些方法,并且我确定我没有错误输入或错误复制代码.似乎有一些关于预制文件本身的东西; 一些配置设置或其他我完全错过的东西...这将导致他们正确编译,而我自己制作的文件不会.
由于您在头文件中声明了这些变量,并在每个C++文件中包含头文件,因此每个C++文件都有自己的副本.
通常的做法是不在头文件中声明任何变量.相反,在单个C++文件中声明它们,并将它们声明为extern
您可能需要它们的所有其他文件中的声明.
我之前处理过这种方式的另一种方式,有些人可能会觉得不愉快...在头文件中声明它们,如下所示:
#ifdef MAINFILE #define EXTERN #else #define EXTERN extern #endif EXTERN MYSTRUCT Job_Grunt; EXTERN MYSTRUCT *Grunt = &Job_Grunt; EXTERN MYSTRUCT Job_Uruk; EXTERN MYSTRUCT *Uruk = &Job_Uruk;
然后,在您的一个 C++文件中,添加一个...
#define MAINFILE
......在你的#include
台词之前.这将照顾一切,并且(在我个人看来)比在每个文件中重新声明所有变量要好得多.
当然,真正的解决方案根本不是使用全局变量,而是当你刚刚开始时很难实现.
定义变量时,编译器会为该变量留出内存.通过在头文件中定义变量,并将该文件包含到所有源文件中,您将在多个文件中定义相同的变量.
将关键字放在extern
变量定义之前将告诉编译器此变量已在某处定义,并且您只是声明(即命名)变量以便其他文件可以使用它.
因此,在头文件中,您应该通过添加关键字来使所有定义转发声明extern
.
extern MYSTRUCT Job_Grunt; extern MYSTRUCT *Grunt; extern MYSTRUCT Job_Uruk; extern MYSTRUCT *Uruk; extern int Other_data[100];
然后在一个(也是唯一一个)源文件中,正常定义变量:
MYSTRUCT Job_Grunt; MYSTRUCT *Grunt = &Job_Grunt; MYSTRUCT Job_Uruk; MYSTRUCT *Uruk = &Job_Grunt; int Other_data[100];
虽然大多数其他答案对于您看到多个定义的原因是正确的,但术语是不精确的.理解声明与定义是您的问题的关键.
声明宣布存在项目但不会导致实例化.因此,外部声明是声明 - 而不是定义.
定义创建已定义项的实例.因此,如果您在标头中有定义,则会在每个.cpp文件中实例化,从而产生多个定义.定义也是声明 - 即,例如,如果项目的范围限于一个.cpp文件,则不需要单独的声明.
注意:这里使用单词instantiation实际上只适用于数据项.