71

我在像这里这样的地方读到你必须包含 .h 文件而不是 .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

为什么是这样?

4

9 回答 9

71

include所做的是从文件中复制所有内容(这是 or 中的参数)<>""因此当 preproccesor 完成其工作时,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,因此编译器会因为函数重复而“感到困惑”。

于 2013-10-23T16:24:45.327 回答
42

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

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

于 2013-10-23T16:25:59.023 回答
18

当你说#include "foop.cpp"的时候,就好像你已经复制了整个内容foop.cpp并粘贴到了main.cpp

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

当您自己编译时foop.cpp,编译器会发出一个foop.obj包含 function 的可执行代码的代码foo

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

于 2013-10-23T16:26:15.710 回答
8

这归结为定义声明之间的差异。

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

标头通常包含声明;cpp 文件包含定义。当您多次包含具有定义的文件时,您会在链接期间获得重复项。

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

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

于 2013-10-23T16:25:59.823 回答
3

您应该只包含头文件。

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

于 2016-10-26T08:13:25.420 回答
2

因为一个定义规则(可能是1)。

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

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

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


1:“(可能)”我在这里说可能是因为您发布的实际代码应该编译没有错误,但考虑到编译器错误,您发布的代码似乎与您正在编译的代码不完全相同.

于 2013-10-23T16:32:40.243 回答
1

因为您的程序现在包含该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;
}

因此多重​​定义错误

于 2013-10-23T16:24:40.880 回答
0

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

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

我相信您也可以在从命令行编译时排除该文件。

于 2019-11-19T21:23:32.817 回答
-2

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

于 2014-03-12T07:21:41.157 回答