C++ 为什么在 .h 文件中而不是在 .cpp 中使用 #ifndef CLASS_H 和 #define CLASS_H?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3246803/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why use #ifndef CLASS_H and #define CLASS_H in .h file but not in .cpp?
提问by user385261
I have always seen people write
我一直看到人们写
class.h
类.h
#ifndef CLASS_H
#define CLASS_H
//blah blah blah
#endif
The question is, why don't they also do that for the .cpp file that contain definitions for class functions?
问题是,为什么他们不对包含类函数定义的 .cpp 文件也这样做呢?
Let's say I have main.cpp
, and main.cpp
includes class.h
. The class.h
file does not include
anything, so how does main.cpp
know what is in the class.cpp
?
假设我有 main.cpp
,并且main.cpp
包括class.h
。该class.h
文件没有include
任何内容,那么如何main.cpp
知道class.cpp
?
回答by Justin Summerlin
First, to address your first inquiry:
首先,解决您的第一个查询:
When you see this in .hfile:
当您在.h文件中看到此内容时:
#ifndef FILE_H
#define FILE_H
/* ... Declarations etc here ... */
#endif
This is a preprocessor technique of preventing a header file from being included multiple times, which can be problematic for various reasons. During compilation of your project, each .cppfile (usually) is compiled. In simple terms, this means the compiler will take your .cppfile, open any files #included
by it, concatenate them all into one massive text file, and then perform syntax analysis and finally it will convert it to some intermediate code, optimize/perform other tasks, and finally generate the assembly output for the target architecture. Because of this, if a file is #included
multiple times under one .cppfile, the compiler will append its file contents twice, so if there are definitions within that file, you will get a compiler error telling you that you redefined a variable. When the file is processed by the preprocessor step in the compilation process, the first time its contents are reached the first two lines will check if FILE_H
has been defined for the preprocessor. If not, it will define FILE_H
and continue processing the code between it and the #endif
directive. The next time that file's contents are seen by the preprocessor, the check against FILE_H
will be false, so it will immediately scan down to the #endif
and continue after it. This prevents redefinition errors.
这是一种防止头文件被多次包含的预处理器技术,由于各种原因可能会出现问题。在编译项目期间,每个.cpp文件(通常)都会被编译。简单来说,这意味着编译器将获取您的.cpp文件,#included
通过它打开任何文件,将它们全部连接成一个庞大的文本文件,然后进行语法分析,最后将其转换为一些中间代码,优化/执行其他任务,并最终为目标架构生成程序集输出。因此,如果一个文件#included
在一个.cpp下多次出现文件,编译器将其文件内容追加两次,因此如果该文件中有定义,您将收到一个编译器错误,告诉您重新定义了一个变量。当文件在编译过程中由预处理器步骤处理时,第一次到达其内容时,前两行将检查是否FILE_H
已为预处理器定义。如果没有,它将定义FILE_H
并继续处理它和#endif
指令之间的代码。下次预处理器看到该文件的内容时,检查结果FILE_H
将为 false,因此它将立即向下扫描到#endif
并在其后继续。这可以防止重新定义错误。
And to address your second concern:
并解决您的第二个问题:
In C++ programming as a general practice we separate development into two file types. One is with an extension of .hand we call this a "header file." They usually provide a declaration of functions, classes, structs, global variables, typedefs, preprocessing macros and definitions, etc. Basically, they just provide you with information about your code. Then we have the .cppextension which we call a "code file." This will provide definitions for those functions, class members, any struct members that need definitions, global variables, etc. So the .hfile declares code, and the .cppfile implements that declaration. For this reason, we generally during compilation compile each .cppfile into an object and then link those objects (because you almost never see one .cppfile include another .cppfile).
在 C++ 编程中,作为一般实践,我们将开发分为两种文件类型。一个扩展名为.h,我们称其为“头文件”。它们通常提供函数、类、结构、全局变量、类型定义、预处理宏和定义等的声明。基本上,它们只是为您提供有关代码的信息。然后我们有.cpp扩展名,我们称之为“代码文件”。这将为那些函数、类成员、任何需要定义的结构成员、全局变量等提供定义。因此.h文件声明代码,而.cpp文件实现该声明。为此,我们一般在编译时编译每个.cpp文件转换成一个对象,然后链接这些对象(因为您几乎从未看到一个.cpp文件包含另一个.cpp文件)。
How these externals are resolved is a job for the linker. When your compiler processes main.cpp, it gets declarations for the code in class.cppby including class.h. It only needs to know what these functions or variables look like (which is what a declaration gives you). So it compiles your main.cppfile into some object file (call it main.obj). Similarly, class.cppis compiled into a class.objfile. To produce the final executable, a linker is invoked to link those two object files together. For any unresolved external variables or functions, the compiler will place a stub where the access happens. The linker will then take this stub and look for the code or variable in another listed object file, and if it's found, it combines the code from the two object files into an output file and replaces the stub with the final location of the function or variable. This way, your code in main.cpp can call functions and use variables in class.cppIF AND ONLY IF THEY ARE DECLARED IN class.h.
如何解析这些外部变量是链接器的工作。当您的编译器处理main.cpp 时,它通过包含class.h来获取class.cpp 中代码的声明。它只需要知道这些函数或变量是什么样的(这是声明给你的)。因此它会将您的main.cpp文件编译为某个目标文件(称为main.obj)。同理,class.cpp编译成class.obj文件。为了生成最终的可执行文件,调用链接器将这两个目标文件链接在一起。对于任何未解析的外部变量或函数,编译器将在发生访问的地方放置一个存根。链接器然后将使用这个存根并在另一个列出的目标文件中查找代码或变量,如果找到,它将两个目标文件中的代码组合到一个输出文件中,并用函数的最终位置替换存根或多变的。这样,您在 main.cpp 中的代码可以调用函数并使用class.cpp 中的变量,前提是它们在class.h 中声明。
I hope this was helpful.
我希望这可以帮到你。
回答by Martin B
The CLASS_H
is an include guard; it's used to avoid the same header file being included multiple times (via different routes) within the same CPP file (or, more accurately, the same translation unit), which would lead to multiple-definition errors.
这CLASS_H
是一个包含警卫;它用于避免在同一个 CPP 文件(或更准确地说,同一个翻译单元)中多次(通过不同的路由)包含相同的头文件,这会导致多重定义错误。
Include guards aren't needed on CPP files because, by definition, the contents of the CPP file are only read once.
CPP 文件不需要包含保护,因为根据定义,CPP 文件的内容只读取一次。
You seem to have interpreted the include guards as having the same function as import
statements in other languages (such as Java); that's not the case, however. The #include
itself is roughly equivalent to the import
in other languages.
您似乎已经将包含守卫解释为与import
其他语言(例如 Java)中的语句具有相同的功能;然而,情况并非如此。该#include
本身就是大致相当于import
其他语言。
回答by sum1stolemyname
It doesn't - at least during the compilation phase.
它没有 - 至少在编译阶段。
The translation of a c++ program from source code to machine code is performed in three phases:
C++ 程序从源代码到机器代码的翻译分三个阶段执行:
- Preprocessing- The Preprocessor parses all source code for lines beginning with # and executes the directives. In your case, the contents of your file
class.h
is inserted in place of the line#include "class.h
. Since you might be includein your header file in several places, the#ifndef
clauses avoid duplicate declaration-errors, since the preprocessor directive is undefined only the first time the header file is included. - Compilation- The Compiler does now translate all preprocessed source code files to binary object files.
- Linking- The Linker links (hence the name) together the object files. A reference to your class or one of its methods (which should be declared in class.h and defined in class.cpp) is resolved to the respective offset in one of the object files. I write 'one of your object files' since your class does not needto be defined in a file named class.cpp, it might be in a library which is linked to your project.
- 预处理- 预处理器解析所有以 # 开头的行的源代码并执行指令。在您的情况下,文件的内容
class.h
被插入到行的位置#include "class.h
。由于您可能在多个位置包含在头文件中,因此这些#ifndef
子句避免了重复的声明错误,因为仅在第一次包含头文件时未定义预处理器指令。 - 编译- 编译器现在确实将所有预处理的源代码文件转换为二进制目标文件。
- 链接- 链接器将目标文件链接在一起(因此得名)。对您的类或其方法之一(应在 class.h 中声明并在 class.cpp 中定义)的引用解析为目标文件之一中的相应偏移量。我写的,因为没有你的类的对象文件中的一个“需要在一个文件名为class.cpp来定义,它可能在链接到你的项目库。
In summary, the declarations can be shared through a header file, while the mapping of declarations to definitions is done by the linker.
总之,声明可以通过头文件共享,而声明到定义的映射由链接器完成。
回答by Mike Seymour
That's done for header files so that the contents only appear once in each preprocessed source file, even if it's included more than once (usually because it's included from other header files). The first time it's included, the symbol CLASS_H
(known as an include guard) hasn't been defined yet, so all the contents of the file are included. Doing this defines the symbol, so if it's included again, the contents of the file (inside the #ifndef
/#endif
block) are skipped.
这是对头文件完成的,因此内容在每个预处理过的源文件中只出现一次,即使它被包含多次(通常是因为它是从其他头文件中包含的)。第一次包含时,符号CLASS_H
(称为包含保护)尚未定义,因此文件的所有内容都包含在内。这样做定义了符号,因此如果再次包含它,则文件的内容(在#ifndef
/#endif
块内)将被跳过。
There's no need to do this for the source file itself since (normally) that's not included by any other files.
无需为源文件本身执行此操作,因为(通常)它不包含在任何其他文件中。
For your last question, class.h
should contain the definition of the class, and declarations of all its members, associated functions, and whatever else, so that any file that includes it has enough information to use the class. The implementations of the functions can go in a separate source file; you only need the declarations to call them.
对于您的最后一个问题,class.h
应该包含类的定义,及其所有成员的声明、关联的函数以及其他任何内容,以便包含它的任何文件都有足够的信息来使用该类。函数的实现可以放在一个单独的源文件中;你只需要声明来调用它们。
回答by Brian R. Bondy
That's the distinction between declaration and definition. Header files typically include just the declaration, and the source file contains the definition.
这就是声明和定义之间的区别。头文件通常只包含声明,而源文件包含定义。
In order to use something you only need to know it's declaration not it's definition. Only the linker needs to know the definition.
为了使用某些东西,您只需要知道它是声明而不是定义。只有链接器需要知道定义。
So this is why you will include a header file inside one or more source files but you won't include a source file inside another.
所以这就是为什么你会在一个或多个源文件中包含一个头文件,但你不会在另一个中包含一个源文件。
Also you mean #include
and not import.
你的意思是#include
而不是进口。
回答by Igor Oks
main.cppdoesn't have to know what is in class.cpp. It just has to know the declarations of the functions/classes that it goes to use, and these declarations are in class.h.
main.cpp不必知道class.cpp 中的内容。它只需要知道它要使用的函数/类的声明,这些声明在class.h 中。
The linker links between the places where the functions/classes declared in class.hare used and their implementations in class.cpp
使用class.h中声明的函数/类的位置与其在class.cpp 中的实现之间的链接器链接
回答by Kate Gregory
.cpp
files are not included (using #include
) into other files. Therefore they don't need include guarding. Main.cpp
will know the names and signatures of the class that you have implemented in class.cpp
only because you have specified all that in class.h
- this is the purpose of a header file. (It is up to you to make sure that class.h
accurately describes the code you implement in class.cpp
.) The executable code in class.cpp
will be made available to the executable code in main.cpp
thanks to the efforts of the linker.
.cpp
文件不包含(使用#include
)到其他文件中。因此他们不需要包括守卫。Main.cpp
将知道您在其中实现的类的名称和签名,class.cpp
因为您已在其中指定了所有内容class.h
- 这是头文件的目的。(由您来确保class.h
准确描述您在 中实现的代码class.cpp
。)多亏了链接器的努力, 中的可执行代码class.cpp
将对 中的可执行代码可用main.cpp
。
回答by autistic
It is generally expected that modules of code such as .cpp
files are compiled once and linked to in multiple projects, to avoid unnecessary repetitive compilation of logic. For example, g++ -o class.cpp
would produce class.o
which you could then link from multiple projects to using g++ main.cpp class.o
.
通常希望.cpp
文件等代码模块编译一次,并链接到多个项目中,以避免不必要的逻辑重复编译。例如,g++ -o class.cpp
将产生class.o
然后您可以从多个项目链接到使用g++ main.cpp class.o
.
We could use #include
as our linker, as you seem to be implying, but that would just be silly when we know how to link properly using our compiler with less keystrokes and less wasteful repetition of compilation, rather than our code with more keystrokes and more wasteful repetition of compilation...
我们可以将其#include
用作我们的链接器,正如您似乎暗示的那样,但是当我们知道如何使用我们的编译器以更少的击键和更少的浪费重复编译正确链接时,这将是愚蠢的,而不是我们的代码具有更多的击键和更多浪费重复编译...
The header files are still required to be included into each of the multiple projects, however, because this provides the interface for each module. Without these headers the compiler wouldn't know about any of the symbols introduced by the .o
files.
但是,仍然需要将头文件包含在多个项目中的每一个中,因为这为每个模块提供了接口。如果没有这些头文件,编译器就不会知道.o
文件引入的任何符号。
It is important to realise that the header files are what introduce the definitions of symbols for those modules; once that is realised then it makes sense that multiple inclusions could cause redefinitions of symbols (which causes errors), so we use include guards to prevent such redefinitions.
重要的是要意识到头文件是为这些模块引入符号定义的内容;一旦实现,那么多个包含可能会导致符号的重新定义(这会导致错误)是有道理的,因此我们使用包含保护来防止这种重新定义。
回答by Quonux
its because of Headerfiles define what the class contains (Members, data-structures) and cpp files implement it.
因为 Headerfiles 定义了类包含的内容(成员、数据结构)和 cpp 文件实现它。
And of course, the main reason for this is that you could include one .h File multiple times in other .h files, but this would result in multiple definitions of a class, which is invalid.
当然,这样做的主要原因是你可以在其他 .h 文件中多次包含一个 .h 文件,但这会导致一个类的多个定义,这是无效的。