为什么在.h文件中使用#ifndef CLASS_H和#define CLASS_H而在.cpp中没有?

use*_*261 128 c++

我一直看到人们写

class.h

#ifndef CLASS_H
#define CLASS_H

//blah blah blah

#endif
Run Code Online (Sandbox Code Playgroud)

问题是,为什么它们也不会为包含类函数定义的.cpp文件执行此操作?

假设我有 main.cpp,main.cpp包括class.h.该class.h文件不会导入任何内容,所以如何main.cpp知道该中的内容class.cpp

Jus*_*lin 283

首先,解决您的第一个询问:

当你在.h文件中看到这个时:

#ifndef FILE_H
#define FILE_H

/* ... Declarations etc here ... */

#endif
Run Code Online (Sandbox Code Playgroud)

这是一种防止头文件被多次包含的预处理器技术,由于各种原因这可能是有问题的.在编译项目期间,会编译每个.cpp文件(通常).简单来说,这意味着编译器将获取您的.cpp文件,#included通过它打开任何文件,将它们全部连接成一个大型文本文件,然后执行语法分析,最后将它转换为某些中间代码,优化/执行其他任务,最后生成目标体系结构的程序集输出.因此,如果一个文件#included在一个.cpp下多次文件,编译器将附加其文件内容两次,因此如果该文件中有定义,您将收到编译器错误,告诉您重新定义了一个变量.当编译过程中的预处理器步骤处理文件时,第一次到达其内容时,前两行将检查是否FILE_H已为预处理器定义.如果没有,它将定义FILE_H并继续处理它与#endif指令之间的代码.下次预处理器看到该文件的内容时,检查FILE_H将为false,因此它将立即向下扫描#endif并在其后继续.这可以防止重新定义错误.

并解决你的第二个问题:

在C++编程中,作为一般实践,我们将开发分为两种文件类型.一个是.h的扩展名,我们将其称为"头文件".它们通常提供函数,类,结构,全局变量,typedef,预处理宏和定义等的声明.基本上,它们只是为您提供有关代码的信息.然后我们有.cpp扩展名,我们将其称为"代码文件".这将为那些函数,类成员,任何需要定义的结构成员,全局变量等提供定义.因此.h文件声明代码,.cpp文件实现该声明.出于这个原因,我们一般在编译期间编译每个.cpp将文件放入对象然后链接这些对象(因为您几乎从未看到一个.cpp文件包含另一个.cpp文件).

如何解决这些外部因素是链接器的工作.当你的编译器处理main.cpp中,它获得的声明中的代码class.cpp通过包括class.h.它只需要知道这些函数或变量是什么样的(这是声明给你的).所以它将你的main.cpp文件编译成一些目标文件(称之为main.obj).类似地,class.cpp被编译成class.obj文件.要生成最终的可执行文件,需要调用链接器将这两个目标文件链接在一起.对于任何未解析的外部变量或函数,编译器将在发生访问的位置放置一个存根.然后链接器将获取此存根并在另一个列出的目标文件中查找代码或变量,如果找到它,它会将两个目标文件中的代码组合到一个输出文件中,并将该存根替换为该函数的最终位置或变量.这样,main.cpp中的代码可以调用函数并在class.cpp IF中使用变量,只要它们class.h中声明为止.

我希望这可以帮到你.


Mar*_*n B 12

CLASS_H是一个包括守卫 ; 它用于避免在同一CPP文件(或更准确地说,相同的翻译单元)中多次(通过不同路由)包含相同的头文件,这将导致多重定义错误.

CPP文件不需要包含防护,因为根据定义,CPP文件的内容只读取一次.

您似乎已将include guards解释为与import其他语言(例如Java)中的语句具有相同的功能; 但事实并非如此.它#include本身大致相当于import其他语言.

  • "在相同的CPP文件中"应该在"相同的翻译单元内"阅读. (2认同)

Bri*_*ndy 5

这就是声明和定义之间的区别。头文件通常只包含声明,而源文件包含定义。

为了使用某些东西,您只需要知道它是声明而不是定义。只有链接器需要知道定义。

所以这就是为什么你会在一个或多个源文件中包含一个头文件,但你不会在另一个中包含一个源文件。

你的意思是#include而不是进口。


sum*_*ame 5

它没有 - 至少在编译阶段.

将c ++程序从源代码转换为机器代码分为三个阶段:

  1. 预处理 - 预处理器解析以#开头的行的所有源代码并执行指令.在您的情况下,class.h插入文件的内容代替行 #include "class.h.由于您可能在多个位置包含头文件,因此#ifndef子句可以避免重复的声明错误,因为预处理器指令仅在第一次包含头文件时未定义.
  2. 编译 - 编译器现在将所有预处理的源代码文件转换为二进制目标文件.
  3. 链接 - 链接器链接(因此名称)链接目标文件.对类或其某个方法(应在class.h中声明并在class.cpp中定义)的引用将解析为其中一个目标文件中的相应偏移量.我写的"你的对象文件之一",因为你的类并不需要在一个文件名为class.cpp来定义,它可能在链接到你的项目库.

总之,声明可以通过头文件共享,而声明到定义的映射是由链接器完成的.