我是一名经验丰富的程序员,但只有高级语言;我现在正在用C ++做我的第一个非常大的项目。
我有两个课程,ClassA
和ClassB
; ClassA
是ClassBs
的索引,因此ClassA
需要知道ClassB
是什么来构建数组,以及{{1}需要知道ClassB
是什么,以便在更改内容时更新索引。这两个类都属于他们自己的.h& .cpp文件。
我认为从另一个中包含每个只会导致无限递归,所以我决定在ClassA
的开头添加#include "ClassA.cpp"
和#include "ClassB.cpp"
;但这样做只会导致编译器警告这些文件中每个类和方法的多个定义。
经过一些实验,我发现包含main.cpp
和ClassA.h
会产生所需的行为 - 但这没有任何意义,我只包括这些类的原型。当然,真正使它们成为现实的代码永远不会混杂在一起?但确实如此。
我不明白这里发生了什么?为什么包括ClassB.h
也会使ClassA.h
的实际代码显示出来?为什么包含ClassA
导致ClassA.cpp
的每个包含都会触发“多个定义”错误,即使它们位于标题屏蔽或其所谓的内容中?
答案 0 :(得分:6)
缺少的步骤是链接器不会看到ClassA.cpp
和ClassB.cpp
中的定义,除非这些文件在某些时候也编译。如果你做了这样的事情:
g++ main.cpp ClassA.cpp ClassB.cpp
然后,ClassA.cpp
中ClassB.cpp
和main.cpp
中对定义的所有引用都将得到解决。但是,如果你只做了
g++ main.cpp
然后链接器不知道在ClassA.cpp
和ClassB.cpp
中找到定义的位置,您可能会收到错误。
如果你正在使用IDE,那么这个细节对你来说是隐藏的:IDE确保只要你将.cpp文件添加到你的“项目”中,它就会在你构建项目时被编译成最终的二进制文件
答案 1 :(得分:1)
这就是C ++的设计方式:
您的类现在不需要其他类的原型,因此您不必包含多个标题。
为什么会这样?好吧,整个应用程序的编译是两个步骤的组合:代码本身的编译然后链接(实际上,在这些之前还有第三步:预处理,但我们可以将这一步视为代码编译的一部分)。
示例函数调用:知道存在具有特定原型类型的函数就足够了(异常:内联函数!)。然后编译器可以生成执行函数调用所需的所有代码,除了函数的实际地址 - 它为它留下了某种占位符。
然后,链接器将编译步骤中生成的所有代码组合到一个单元中。现在知道每个功能的位置,它可以将它们的实际地址填入占位符,无论它们出现在哪里。
答案 2 :(得分:1)
对于每个*.obj
文件,C ++代码被编译为.cpp
,并且link
进程将obj文件编译为可执行文件。
永远不要包含* .cpp,因为它通常会导致重定义问题。
对于每个* .h文件,添加一个宏以避免多个包括:
#ifndef XXX_H
#define XXX_H
//your code goes here
#endif