由于在多个 cpp 中包含相同标头而导致重复的多个定义错误

发布于 2024-07-07 20:45:34 字数 1903 浏览 9 评论 0原文

因此,无论我做什么,我似乎都无法避免 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 开头读取包含时,它会重新定义头文件,这导致它吐出一行又一行:

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 文件。 我尝试将结构和结构变量的定义移至 cpp 文件,但其他 cpp 文件无法看到它们或使用它们,这非常重要,因为我需要项目中的所有文件都能够工作与这些结构。

但是关于这个问题的最令人困惑的部分需要更多的解释:

我在这个项目中设置这些多个文件的方式与我正在使用的书相同,约翰·S·哈伯(John S. Harbour)的《全合一游戏编程》。 当我为书中的示例项目创建文件时,我遇到了完全相同的问题,该文件要求同一项目中的多个 cpp 包含一个标头。

我可以把它们从书中逐字逐句地打出来,我的意思是逐字逐句......
我会收到项目中每个 cpp 的一系列 MD 错误。

如果我从本书附带的 CD 加载示例项目,它会毫无问题地编译和运行,尽管文件本身以及项目选项从外观上看与我创建的文件完全相同。

如果我创建自己的项目文件,并简单地从 CD 添加示例项目的源文件和头文件,这也将编译并运行,尽管我发现这些文件和我的项目没有任何区别。

然后,我尝试制作自己的项目文件,然后创建空白源文件和头文件并将它们添加到其中,然后通过从它们对应的 CD 上的文件复制并粘贴其内容来填充它们(相同那些曾经有效的)。 果然,我得到了同样的结果……一行又一行的 MD 错误消息。

我完全困惑了。 我已经多次重复了所有这些方法,并且确信我没有输错或复制代码。 预制文件本身似乎有一些东西; 一些配置设置或其他我完全丢失的东西......这将导致它们正确编译,而我自己制作的文件则不会。

So, no matter what I seem to do, I cannot seem to avoid having Dev C++ spew out numerous Multiple Definition errors as a result of me including the same header file in multiple source code files in the same project. I'd strongly prefer to avoid having to dump all my source code into one file and only include the header once, as that's going to make my file very long and difficult to manage.

Essentially, this is what's going on:

#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

Example Cpp File (They pretty much all look something like this):

#include "myheader.h"

void load_jobs(){

Grunt->blah = 1;
Grunt->blah2 = 14;

Uruk->blah = 2;
Uruk->blah2 = 15;

return; }

Bear in mind that I have about 5 cpp files that include this one header, each one dealing with a different type of struct found in the header file. In this example there was only the one struct containing a couple of members, when there are about 4-6 different structs with many more members in the actual header file. All the files I've included it in follow the same formula as you see in this example here.

Now I understand that the header guard only stops each individual cpp file from including the header file more than once. What would seem to be happening is that when the compiler reads the include at the start of each cpp, it defines the header file all over again, which is causing it to spit out lines and lines of:

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

I'll see a set of this for just about every cpp file in the project which includes the header. I've tried moving the definitions of the struct and the struct variables to the cpp files, but then the other cpp files cannot see them or work with them, which is very important as I need all files in the project to be able to work with these structs.

But the single most confusing part about this problem requires a little more explanation:

The way I'm setting up these multiple files in this project is identical to the book I'm working with, All In One Game Programming by John S. Harbour. I ran into the exact same problems when I created the files for example projects in the book which called for one header included by multiple cpps in the same project.

I could type them out, word for word from the book, and I do mean word for word...
and I'd get the series of MD errors for every cpp in the project.

If I loaded the example project from the CD included with the book, it would compile and run without a problem, allthough the files themselves, as well as the project options, were by all appearances identical to the ones I had created.

If I created my own project file, and simply added the source and header files for the example project from the CD, this, too, would also compile and run, though I can find no difference between those and mine.

So then, I tried making my own project file, then creating the blank source and header files and adding them to it, and then filling them by copying and pasting their contents from the files on the CD they were meant to correspond to(the same ones that had worked).
And sure enough, I'd get the same thing...lines and lines of MD error messages.

I'm absolutely baffled. I've repeated all these methods multiple times, and am certain I'm not mistyping or miscopying the code. There just seems to be something about the premade files themselves; some configuration setting or something else I'm missing entirely...that will cause them to compile correctly while the files I make myself won't.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

淡写薰衣草的香 2024-07-14 20:45:34

由于您在头文件中声明这些变量,并将头文件包含在每个 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;

添加...

#define MAINFILE

然后,在一个 C++ 文件中,在#include 行之前 ...。 这将处理所有事情,并且(在我个人看来)比必须重新声明每个文件中的所有变量要好得多。

当然,真正的解决方案是根本不使用全局变量,但当您刚刚开始时,这是很难实现的。

Since you're declaring those variables in the header file, and including the header file in each C++ file, each C++ file has its own copy of them.

The usual way around this is to not declare any variables within header files. Instead, declare them in a single C++ file, and declare them as extern in all the other files that you might need them in.

Another way I've handled this before, which some people might consider unpleasant... declare them in the header file, like this:

#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;

Then, in one of your C++ files, add a...

#define MAINFILE

...before your #include lines. That will take care of everything, and is (in my personal opinion) a lot nicer than having to redeclare all of the variables in every file.

Of course, the real solution is not to use global variables at all, but when you're just starting out that's hard to achieve.

转瞬即逝 2024-07-14 20:45:34

当您定义变量时,编译器会为该变量留出内存。 通过在头文件中定义变量,并将该文件包含到所有源文件中,您可以在多个文件中定义相同的变量。

在变量定义之前放置关键字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];

When you define a variable, the compiler sets aside memory for that variable. By defining a variable in the header file, and including that file into all your source files, you are defining the same variable in multiple files.

Putting the keyword extern before a variable definition will tell the compiler that this variable has already been defined somewhere, and that you are only declaring (i.e. naming) the variable so that other files can use it.

So in your header file you should make all your definitions forward declarations by adding the extern keyword.

extern MYSTRUCT Job_Grunt;
extern MYSTRUCT *Grunt;
extern MYSTRUCT Job_Uruk;
extern MYSTRUCT *Uruk;

extern int Other_data[100];

And then in one (and only one) of your source files, define the variables normally:

MYSTRUCT Job_Grunt;
MYSTRUCT *Grunt = &Job_Grunt;
MYSTRUCT Job_Uruk;
MYSTRUCT *Uruk = &Job_Grunt;

int Other_data[100];
困倦 2024-07-14 20:45:34

虽然大多数其他答案对于您看到多个定义的原因都是正确的,但术语并不精确。 理解声明与定义是解决问题的关键。

声明宣告某项的存在,但不会导致实例化。 因此 extern 语句是声明,而不是定义。

定义创建所定义项的实例。 因此,如果标头中有一个定义,它将在每个 .cpp 文件中实例化,从而产生多个定义。 定义也是声明 - 即,如果项的范围仅限于一个 .cpp 文件,则不需要单独的声明。

注意:这里使用的“实例化”一词实际上仅适用于数据项。

While most of the other answers are correct as to why you are seeing multiple definitions, the terminology is imprecise. Understanding declaration vs. definition is the key to your problem.

A declaration announces the existence of an item but does not cause instantiation. Hence the extern statements are declarations - not definitions.

A definition creates an instance of the defined item. Hence if you have a definition in a header it is instantiated in each .cpp file, resulting in the multiple definitions. Definitions are also declarations - i.e. no separate declaration is needed if for instance the scope of the item is limited to one .cpp file.

Note: the use of the word instantiation here really only applies to data items.

慕烟庭风 2024-07-14 20:45:34

您需要在头文件中将变量定义为 extern,然后也在 cpp 文件中定义它们。 即:

extern MYSTRUCT Job_Grunt;

在您的头文件中,然后在项目的 cpp 文件中正常声明它们。

头文件仅用于定义,当您在头文件中实例化变量时,每次将头包含在项目中时,它都会尝试实例化它。 使用 extern 指令告诉编译器它只是一个定义,并且实例化是在其他地方完成的。

You need to define your variables as extern in the header file, and then define them in a cpp file as well. i.e.:

extern MYSTRUCT Job_Grunt;

in your header file, and then in a cpp file in your project declare them normally.

The header file is only for definitions, when you instantiate a variable in the header file it will try to instantiate it every time the header is included in your project. Using the extern directive tells the compiler that it's just a definition and that the instantiation is done somewhere else.

踏雪无痕 2024-07-14 20:45:34

对于 .h 文件中定义的函数,我也收到此错误。 头文件的目的不是声明类,而是定义项目中各个位置所需的一些函数。 (我可能会混淆“定义”和“声明”的用法,但我希望我能给出主要想法。)当我在给出“多重定义”错误的函数定义之前放置“内联”关键字时,避免了错误。

I also received this error for a function defined in a .h file. The header file was not intended to make declarations of a class but definitions of some functions which are needed in various places in a project. (I may confuse the "definition" and "declaration" usages, but i hope i could give the main idea.) When I put an "inline" keyword just before the definition of the function which give the "multiple definitions" error, the error is avoided.

日久见人心 2024-07-14 20:45:34

为了扩展杰拉尔德所说的内容,标头正在定义结构的实例(这不是您想要的)。 这导致包含标头的每个编译单元(cpp 文件)获取其自己的结构实例版本,这会在链接时导致问题。

正如 Gerald 所说,您需要在标头中定义对结构的引用(使用“extern”),并在项目中拥有一个用于实例化实例的 cpp 文件。

To expand on what Gerald said, the header is defining an instance of the struct (which is not what you want). This is causing each compilation unit (cpp file) which includes the header to get its own version of the struct instance, which causes problems at link time.

As Gerald said, you need to define a reference to the struct (using 'extern') in the header, and have one cpp file in your project which instantiates the instance.

燃情 2024-07-14 20:45:34

我前段时间也遇到过这个问题。 让我尝试解释一下是什么解决了这个问题。
我有一个 global.h 文件,其中包含所有声明,并且需要包含在每个 cpp 文件中。 我没有将其包含在每个 .cpp 中,而是将其包含在 .h 中。 我的所有“.h”文件都添加了#ifndef 和#define 行,并以#endif 结尾。 这解决了MD问题。 希望这也适合你。

I too was having this problem some time back. Let me try to explain what solved it.
I had a global.h file which had all declaration and need to be included in every cpp file. Instead of including it in every .cpp, I included it in .h. All my ".h" files I have added the lines #ifndef and #define and ended with #endif. This solved MD problem. Hope this works for you too.

提笔落墨 2024-07-14 20:45:34

这对我有用:将源链接到单独的库中。 (我的问题不是创建一个程序,而是创建一个/多个库。)然后,我(成功)将一个程序与我创建的两个库链接起来。

我在同一个源文件中有两组函数(一组依赖于另一组),并在同一个头文件中声明。 然后我尝试将两个函数集分开在两个头文件+源文件中。

我尝试过使用 #pragma 一次,并使用 #ifndef ... #define ... #endif 包含防护。 我还在头文件中将变量和函数定义为 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 链接起来。 就我的具体情况而言,这没有什么区别; 但除此之外我无法让他们一起工作。

This is what worked for me: linking the sources into separate libraries. (My problem was not with creating a program but one/many libraries.) I then linked (successfully) one program with the two libraries I created.

I had two sets of functions (with one depending on the other) in the same source file, and declared in the same single header file. Then I tried to separate the two function sets in two header+source files.

I tried with both #pragma once and include guards with #ifndef ... #define ... #endif. I also defined the variables and functions as extern in the header files.

As Steve Fallows pointed out, the problem isn't with the compilation but rather with linkage. In my particular problem, I could get away with having two sets of functions, each in its own source file, compiling and then linking into two separate libraries.

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

This forces me to link my programs with both libgf.so and libfather.so. In my particular case it makes no difference; but otherwise I couldn't get them to work together.

心房的律动 2024-07-14 20:45:34

GCC 3.4 及更高版本支持#pragma Once。 只需将 #pragma Once 放在代码顶部,而不是使用包含防护。 这可能会更成功,也可能不会更成功,但值得一试。 不,这并不(总是)完全等同于包含防护。

GCC 3.4 and up supports #pragma once. Just put #pragma once at the top of your code instead of using include guards. This may or may not be more successful, but it's worth a shot. And no, this is not (always) precisely equivalent to an include guard.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文