包括 .cpp 文件

Posted

技术标签:

【中文标题】包括 .cpp 文件【英文标题】:Including .cpp files 【发布时间】:2013-10-23 16:21:38 【问题描述】:

我在here 之类的地方读到过,您必须包含 .h 文件而不是 .cpp 文件,否则会出现错误。比如

main.cpp

#include <iostream>
#include "foop.h"

int main(int argc, char *argv[])

int x=42;
std::cout << x <<std::endl;
std::cout << foo(x) << std::endl;
return 0;

foop.h

#ifndef FOOP_H
#define FOOP_H
int foo(int a);
#endif

foop.cpp

int foo(int a)
    return ++a;

有效,但如果我将 #include "foop.h" 替换为 #include "foop.cpp" 会出现错误(使用 Dev C++ 4.9.9.2,Windows):

multiple definition of foo(int)
first defined here

这是为什么?

【问题讨论】:

这个例子应该编译得很好。有没有遗漏的细节? 您是否只包含 .cpp?在这种简单的情况下应该允许这样做。包括 .h 和 .cpp 显然乘法定义了函数 【参考方案1】:

include 所做的是复制文件中的所有内容(这是 &lt;&gt;"" 中的参数),因此当预处理器完成其工作时 main.cpp 将如下所示:

// iostream stuff

int foo(int a)
    return ++a;


int main(int argc, char *argv[])

   int x=42;
   std::cout << x <<std::endl;
   std::cout << foo(x) << std::endl;
   return 0;

所以 foo 会在 main.cpp 中定义,但在 foop.cpp 中也存在定义,所以编译器会因为函数重复而“混淆”。

【讨论】:

我想你可以通过使用#pragma once 指令来解决它。 @xorguy foop.c 和 foop.h 是如何相互了解的?我理解您在回答中所说的内容,但我仍然不清楚 main.cpp 如何知道 foo 的定义? main.cpp 包括 foop.h ware 只是声明而不是 foo 的定义。那么 main.cpp 是如何知道 foo 的定义的呢? @WakanTanka 如果方法正确(即包括头文件),main.cpp知道foo.cpp,因此不会抛出错误。这就是我们使用头文件的原因。在他的回答中,@xorguy 使用了一个示例,其中main.cpp 是#include cpp 文件。在这种情况下,因为在main.cppfoo.cpp 中都定义了相同的函数,所以会引发错误。 编译器会很高兴,它是链接器会显示关于重复实现的错误。同时,如果您从链接中排除 foo.cpp - 即使没有#pragma once,构建也会成功。但是在大型项目中这样做是浪费编译器时间,因为所有代码都会在任何更改时重新编译。【参考方案2】:

不鼓励包含 .cpp 文件的原因有很多,但并非严格禁止。您的示例应该可以正常编译。

问题可能是您正在编译 main.cpp 和 foop.cpp,这意味着 foop.cpp 的两个副本被链接在一起。链接器抱怨重复。

【讨论】:

【参考方案3】:

当你说#include "foop.cpp"时,就好像你复制了foop.cpp的全部内容并粘贴到main.cpp中。

因此,当您编译 main.cpp 时,编译器会发出一个 main.obj,其中包含两个函数的可执行代码:mainfoo

当您编译@​​987654328@ 本身时,编译器会发出一个foop.obj,其中包含函数foo 的可执行代码。

当您将它们链接在一起时,编译器会看到函数foo 的两个定义(一个来自main.obj,另一个来自foop.obj)并抱怨您有多个定义。

【讨论】:

【参考方案4】:

这归结为定义声明之间的区别。

您可以在不同的翻译单元或同一个翻译单元中多次声明函数和变量。一旦你声明了一个函数或一个变量,你就可以从那时起使用它。 您只能在所有翻译单元中定义一次非静态函数或变量。多次定义非静态项会导致链接器错误。

标头通常包含声明; cpp 文件包含定义。如果您多次包含具有定义的文件,则在链接期间会出现重复项。

在您的情况下,一个定义来自foo.cpp,另一个定义来自main.cpp,其中包括foo.cpp

注意:如果您将foo 更改为static,则不会出现链接错误。尽管没有错误,但这不是一件好事。

【讨论】:

这几乎就是我一直在寻找的答案。我是 C++ 的新手,例如,如何声明一个类?我应该使用头文件吗?那么如何定义该类以在其他文件中使用?对不起这个问题,无关,但我没有找到合适的帮助:( @TheProHands Take a look at this article.【参考方案5】:

你应该只包含头文件。

如果包含头文件,头文件会自动查找.cpp 文件。 --> 这个过程是由 LINKER 完成的。

【讨论】:

这个答案完美地解释了问题中解决的问题。 @pasha 这个答案完全忽略了这个主题的细微差别,同时实际上是错误的。 @enedil 感谢您指出这一点,在 google 上花时间后我现在同意,但请解释一下为什么会这样,也许我可以学到一些其他的东西 @pasha 我曾经读过这个答案,并认为它也是正确的......这是错误的,有点误导。没有这样的“头文件找到.cpp文件”的东西。因为只有 .cpp 文件被编译。头文件只是留在那里并等待一些源文件包含它们。观看此视频以了解更多信息youtube.com/watch?v=3tIqpEmWMLI @Rick 感谢您的链接【参考方案6】:

因为One Definition Rule(可能1)。

在 C++ 中,每个非内联对象和函数必须在程序中具有完全 一个 定义。通过#includeing 定义foo(int) 的文件(CPP 文件),它在foop.cpp 为#included 的每个文件和foop.cpp 本身中都定义了(假设foop.cpp 已编译) )。

您可以创建一个函数inline 来覆盖此行为,但我不建议在这里这样做。我从未见过有必要甚至需要#include CPP 文件的情况。

在某些情况下,需要包含对某事物的定义。当您尝试将模板的定义与模板的声明分开时尤其如此。在这些情况下,我将文件命名为 HPP 而不是 CPP 来表示差异。


1:“(可能)”我在这里说可能是因为您发布的实际代码应该可以正确编译,但考虑到编译器错误,您发布的代码似乎不是一模一样和你正在编译的代码一样。

【讨论】:

【参考方案7】:

因为您的程序现在包含 foo 函数的两个副本,一个在 foo.cpp 中,一个在 main.cpp 中

将#include 视为编译器将该文件的内容复制/粘贴到您的代码中的指令,因此您最终会得到一个经过处理的 main.cpp,如下所示

#include <iostream> // actually you'll get the contents of the iostream header here, but I'm not going to include it!
int foo(int a)
    return ++a;


int main(int argc, char *argv[])

int x=42;
std::cout << x <<std::endl;
std::cout << foo(x) << std::endl;
return 0;

和 foo.cpp

int foo(int a)
    return ++a;

因此多重定义错误

【讨论】:

main.cpp 中,有没有定义foo()foo() 是在foop.cpp 文件中定义的。当我们#include&lt;foop.cpp&gt; 时,为什么foop.cpp 中的代码只被粘贴到main.cpp 文件中一次,而foo() 被包含两次?【参考方案8】:

所以我发现,如果您从 Visual Studios 编译,您只需从最终构建(您从中扩展的那个)中排除包含的 .cpp 文件:

Visual Studios:.cpp 文件 > 右键 > 属性 > 配置属性 > 常规 > 从构建中排除 >

相信你也可以在命令行编译的时候排除文件。

【讨论】:

【参考方案9】:

使用“.h”方法更好 但是如果你真的想包含 .cpp 文件,那么在 foo.cpp 中将 foo(int) 设为静态

【讨论】:

以上是关于包括 .cpp 文件的主要内容,如果未能解决你的问题,请参考以下文章

包括 .cpp 而不是 header(.h)

从cpp文件创建头文件.h [关闭]

编号的cpp文件怎么运行

Makefile 编译 c 和 cpp 文件

c中头文件在cpp文件里引用和.h文件引用的思考

在 2 个 cpp 文件中定义一个 C++ 类?