我在使用类时尝试了一些C++代码,这个问题困扰着我。我已经创建了一个包含类定义的头文件和一个包含实现的cpp文件。如果我在另一个cpp文件中使用这个类,为什么要包含头文件而不是包含包含类实现的cpp文件呢?如果我包含类实现文件,那么类头文件应该自动导入(因为我已经在实现文件中包含了头文件),这不更自然吗?很抱歉如果这是一个愚蠢的问题,我真的很想知道为什么大多数人包含.h而不是.cpp文件,后者似乎更自然(我至少知道Python)。这是历史原因还是与程序组织有关的技术原因,或者其他原因?
int foo(int a, float b);
和 foo.cpp
#include "foo.h"
int foo(int a, float b) { /* implementation */ }
以及bar.cpp
#include "foo.h"
int bar(void) {
int c = foo(1, 2.1);
}
当你编译foo.cpp
时,它会变成foo.o
,编译bar.cpp
也是类似的生成bar.o
。在编译过程中,编译器需要检查foo.cpp
中函数foo()
的定义,确保其与bar.cpp
中对foo()
的使用一致(即以int
和float
作为参数并返回一个int
)。它通过在两个.cpp
文件中都包含相同的头文件来实现这一点。如果定义和使用都符合头文件中的声明,则它们必须相互一致。foo()
的实现包含在bar.o
中。它只会包含一个汇编语言指令去调用foo
。因此,在创建bar.o
时,它不需要了解foo.cpp
的内容。但是,在链接阶段(即在编译之后),连接器确实需要知道foo()
的实现,因为它将包含该实现到最终程序中,并将call foo
指令替换为call 0x109d9829
(或者连接器决定的函数foo()
在内存中的地址)。foo()
的实现(在foo.o
中)与对foo()
的使用(在bar.o
中)是否一致——例如,它不检查是否以正确的int
和float
参数调用了foo()
!这种检查在汇编语言中有点困难(至少比在C++源代码中检查要困难),因此连接器依赖于已知编译器已经进行过检查。这就是为什么需要头文件来向编译器提供这些信息的原因。这个魔法是由链接器完成的。每个 .cpp 文件在编译时都会生成一个带有所有导出和导入符号的中间对象文件表。链接器将对它们进行调和。换句话说,您只需包含头文件,每次引用所包含的类时,编译器将把所引用类的签名放入符号表中。
如果您包含 .cpp 文件,则会将相同的代码编译两次,并且会出现链接错误,因为链接器会找到相同的符号,从而产生歧义。
cpp
文件。 - fredoverflow