在多个cpps中包含相同的标题,重复出现多重定义错误
所以,无论我怎么做,似乎都无法避免让Dev C ++在同一个项目中的多个源代码文件中包含相同的头文件,从而导致无数的多重定义错误。 我宁愿避免将我所有的源代码转储到一个文件中,只包含一次头文件,因为这会使我的文件变得非常长,难以pipe理。
基本上,这是发生了什么事情:
#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文件,包括这一个头,每一个处理在头文件中find不同types的结构。 在这个例子中,只有一个结构包含几个成员,当大约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文件。 我已经尝试将结构和结构variables的定义移动到cpp文件,但其他cpp文件不能看到它们或与他们一起工作,这是非常重要的,因为我需要项目中的所有文件能够工作与这些结构。
但是关于这个问题最棘手的部分需要更多的解释:
我在这个项目中设置这些多个文件的方式与我正在使用的书籍John S. Harbor的All In One Game Programming完全相同。 当我在本书中创build示例项目的文件时遇到了完全相同的问题,这些项目需要在同一个项目中调用多个cpp包含的一个头文件。
我可以从这本书中一字一句地打印出来,而且我的意思是一字不差
我会得到项目中每个cpp的一系列MD错误。
如果我从本书附带的CD中加载示例项目,它将编译并运行,没有任何问题,尽pipe这些文件本身以及项目选项与我创build的文件完全相同。
如果我创build了自己的项目文件,并且简单地从CD中添加了示例项目的源文件和头文件,那么也可以编译和运行,尽pipe我可以发现它们和我的没有区别。
于是,我尝试制作自己的项目文件,然后创build空白的源文件和头文件,并将它们添加到它,然后通过复制和粘贴他们的内容从他们的意图对应的CD上的文件(相同那些工作)。 果然,我会得到相同的东西…… MD错误消息的线条和线条。
我很困惑。 我已经多次重复了所有这些方法,并确信我不会误认或错误地复制代码。 似乎有关于预制文件本身的东西; 一些configuration设置或其他我完全不知道的东西…这将导致他们编译正确,而我自己的文件不会。
由于您在头文件中声明了这些variables,并且在每个C ++文件中包含头文件,因此每个C ++文件都有自己的副本。
通常的解决方法是不要在头文件中声明任何variables。 相反,将它们声明在一个单独的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
行之前 这将处理所有的事情,并且(在我个人看来)比重新声明每个文件中的所有variables要好得多。
当然, 真正的解决办法是根本不使用全局variables,但是刚开始的时候很难实现。
当你定义一个variables的时候,编译器为这个variables放置内存。 通过在头文件中定义一个variables,并将该文件包含到所有源文件中,就可以在多个文件中定义相同的variables。
把关键字extern
放在一个variables定义之前会告诉编译器这个variables已经在某个地方定义了,而且你只是声明 (即命名)这个variables,以便其他文件可以使用它。
所以在你的头文件中,你应该通过添加extern
关键字来使所有的定义前向声明 。
extern MYSTRUCT Job_Grunt; extern MYSTRUCT *Grunt; extern MYSTRUCT Job_Uruk; extern MYSTRUCT *Uruk; extern int Other_data[100];
然后在一个 (也是唯一的)源文件中,通常定义variables:
MYSTRUCT Job_Grunt; MYSTRUCT *Grunt = &Job_Grunt; MYSTRUCT Job_Uruk; MYSTRUCT *Uruk = &Job_Grunt; int Other_data[100];
虽然大多数其他答案是正确的,为什么你看到多个定义,术语是不精确的。 了解声明与定义是解决问题的关键。
声明宣布一个项目的存在,但不会导致实例化。 因此,外部声明是声明 – 而不是定义。
定义将创build已定义项目的一个实例。 因此,如果在头文件中有一个定义,它会在每个.cpp文件中实例化,从而产生多个定义。 定义也是声明 – 即如果例如项目的范围限于一个.cpp文件,则不需要单独的声明。
注意:在这里使用单词实例化实际上只适用于数据项目。
您需要在头文件中将variables定义为extern,然后在cpp文件中定义它们。 即:
extern MYSTRUCT Job_Grunt;
在你的头文件中,然后在你的项目中的一个cpp文件中正常地声明它们。
头文件仅用于定义,当您在头文件中实例化一个variables时,每次将头包含在项目中时,都会尝试实例化它。 使用extern指令告诉编译器它只是一个定义,而实例化是在其他地方完成的。
我也收到了一个.h文件中定义的函数的这个错误。 头文件不是为了声明一个类,而是定义一些项目中各个地方需要的函数。 (我可能会混淆“定义”和“声明”的用法,但是我希望我能给出主要的想法。)当我在给出“多重定义”错误的函数的定义之前加上“内联”错误被避免。
为了扩展Gerald的说法,头文件定义了一个struct的实例(这不是你想要的)。 这导致每个包含头文件的编译单元(cpp文件)都得到自己版本的结构实例,这会在链接时产生问题。
正如Gerald所说的,你需要在头文件中定义一个对struct的引用(使用'extern'),并在你的项目中有一个cpp文件来实例化这个实例。
我也有一些这个问题。 让我试着解释什么解决了它。 我有一个global.h文件,所有的声明,需要包括在每个CPP文件。 我没有把它包含在每一个.cpp中,而是把它包含在.h中。 我所有的“.h”文件都添加了行号#ifndef和#define,并以#endif结尾。 这解决了MD的问题。 希望这也适用于你。
这对我来说是很有效的:将源代码链接到不同的库中。 (我的问题不是创build一个程序,而是一个/多个库)。然后,我将一个程序与我创build的两个库链接(成功)。
我有两套函数(一个取决于另一个)在同一个源文件中,并在同一个头文件中声明。 然后我试图在两个头文件+源文件中分离两个函数集。
我尝试了两次#pragma,并使用#ifndef包含后卫… #define … #endif。 我还在头文件中将variables和函数定义为extern。
正如史蒂夫·法洛斯(Steve Fallows)指出的那样,问题不在于编辑而在于联系。 在我的特定问题中,我可以放弃使用两套函数,每套函数都在自己的源文件中,编译并链接到两个单独的库中 。
g++ -o grandfather.o -c grandfather.cpp g++ -o father.o -c father.cpp g++ -fPIC -shared -o libgf.so grandfather.o g++ -fPIC -shared -o libfather.so father.o
这迫使我将我的程序与libgf.so和libfather.so链接起来。 在我的具体情况下,这没有什么区别。 但否则我不能让他们一起工作。
GCC 3.4和以上支持#pragma once
。 只需将#pragma once
放在代码的顶部,而不是使用include guard。 这可能会也可能不会更成功,但是值得一试。 不,这不是(总是)完全相当于一个包括后卫。