【问题标题】:Including .cpp files包括 .cpp 文件
【发布时间】: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 显然乘法定义了函数

标签: c++


【解决方案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++ 的新手,例如,如何声明一个类?我应该使用头文件吗?那么如何定义该类以在其他文件中使用?对不起这个问题,无关,但我没有找到合适的帮助:(
      【解决方案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) 设为静态

          【讨论】:

            猜你喜欢
            • 2012-02-08
            • 2012-02-12
            • 1970-01-01
            • 2020-07-05
            • 1970-01-01
            • 1970-01-01
            • 2012-03-04
            • 2013-03-30
            • 1970-01-01
            相关资源
            最近更新 更多