为什么c++有头文件和。cpp文件?


当前回答

c++编译

c++中的编译分为两个主要阶段:

第一种是将“源”文本文件编译为二进制“对象”文件:CPP文件是编译后的文件,编译时不了解其他CPP文件(甚至库),除非通过原始声明或头包含提供给它。CPP文件通常被编译成. obj或. o“对象”文件。 第二步是将所有“目标”文件链接在一起,从而创建最终的二进制文件(库或可执行文件)。

HPP在整个过程中处于什么位置?

一个可怜孤独的CPP文件…

每个CPP文件的编译都独立于所有其他CPP文件,这意味着如果a .CPP需要在B.CPP中定义的符号,例如:

// A.CPP
void doSomething()
{
   doSomethingElse(); // Defined in B.CPP
}

// B.CPP
void doSomethingElse()
{
   // Etc.
}

它不会编译,因为a.c p没有办法知道“doSomethingElse”的存在…除非a . cpp中有声明,比如:

// A.CPP
void doSomethingElse() ; // From B.CPP

void doSomething()
{
   doSomethingElse() ; // Defined in B.CPP
}

然后,如果你有使用相同符号的C.CPP,然后复制/粘贴声明…

复制/粘贴警报!

是的,有个问题。复制/粘贴是危险的,并且难以维护。这意味着如果我们有一些不复制/粘贴的方法,并且仍然声明符号将会很酷…我们该怎么做呢?通过包含一些文本文件,通常以.h, .hxx, .h++或,我更喜欢的c++文件,.hpp结尾:

// B.HPP (here, we decided to declare every symbol defined in B.CPP)
void doSomethingElse() ;

// A.CPP
#include "B.HPP"

void doSomething()
{
   doSomethingElse() ; // Defined in B.CPP
}

// B.CPP
#include "B.HPP"

void doSomethingElse()
{
   // Etc.
}

// C.CPP
#include "B.HPP"

void doSomethingAgain()
{
   doSomethingElse() ; // Defined in B.CPP
}

include是如何工作的?

从本质上讲,包含一个文件将解析并将其内容复制粘贴到CPP文件中。

例如,在下面的代码中,A.HPP报头:

// A.HPP
void someFunction();
void someOtherFunction();

... 来源B.CPP:

// B.CPP
#include "A.HPP"

void doSomething()
{
   // Etc.
}

... 纳入后将成为:

// B.CPP
void someFunction();
void someOtherFunction();

void doSomething()
{
   // Etc.
}

一件小事——为什么要把b.h ppp包括在b.c cpp中?

In the current case, this is not needed, and B.HPP has the doSomethingElse function declaration, and B.CPP has the doSomethingElse function definition (which is, by itself a declaration). But in a more general case, where B.HPP is used for declarations (and inline code), there could be no corresponding definition (for example, enums, plain structs, etc.), so the include could be needed if B.CPP uses those declaration from B.HPP. All in all, it is "good taste" for a source to include by default its header.

结论

因此,头文件是必要的,因为c++编译器不能单独搜索符号声明,因此,您必须通过包含这些声明来帮助它。

最后一句话:你应该在你的HPP文件的内容周围设置头保护,以确保多个包含不会破坏任何东西,但总而言之,我相信HPP文件存在的主要原因已经在上面解释过了。

#ifndef B_HPP_
#define B_HPP_

// The declarations in the B.hpp file

#endif // B_HPP_

或者更简单(尽管不是标准)

#pragma once

// The declarations in the B.hpp file

其他回答

主要原因是将接口从实现中分离出来。头文件声明了一个类(或任何正在实现的类)将做什么,而cpp文件定义了它将“如何”执行这些功能。

这减少了依赖关系,因此使用头文件的代码不一定需要知道实现的所有细节,也不需要知道为此所需的任何其他类/头文件。这将减少编译时间,以及当实现中的某些内容发生更改时所需的重新编译量。

它并不完美,您通常会求助于诸如Pimpl Idiom之类的技术来正确地分离接口和实现,但这是一个良好的开端。

因为设计库格式的人不想为很少使用的信息(如C预处理器宏和函数声明)“浪费”空间。

因为你需要这些信息来告诉你的编译器“当链接器完成它的工作时,这个函数稍后可用”,他们必须拿出第二个文件来存储这些共享信息。

C/ c++之后的大多数语言将这些信息存储在输出中(例如Java字节码),或者它们根本不使用预编译的格式,总是以源代码形式分发并动态编译(Python, Perl)。

因为C语言是这个概念的发源地,它已经有30年的历史了,在当时,它是将多个文件中的代码链接在一起的唯一可行的方法。

今天,这是一个可怕的黑客,它完全破坏了c++中的编译时间,导致无数不必要的依赖关系(因为头文件中的类定义暴露了太多关于实现的信息),等等。

c++编译

c++中的编译分为两个主要阶段:

第一种是将“源”文本文件编译为二进制“对象”文件:CPP文件是编译后的文件,编译时不了解其他CPP文件(甚至库),除非通过原始声明或头包含提供给它。CPP文件通常被编译成. obj或. o“对象”文件。 第二步是将所有“目标”文件链接在一起,从而创建最终的二进制文件(库或可执行文件)。

HPP在整个过程中处于什么位置?

一个可怜孤独的CPP文件…

每个CPP文件的编译都独立于所有其他CPP文件,这意味着如果a .CPP需要在B.CPP中定义的符号,例如:

// A.CPP
void doSomething()
{
   doSomethingElse(); // Defined in B.CPP
}

// B.CPP
void doSomethingElse()
{
   // Etc.
}

它不会编译,因为a.c p没有办法知道“doSomethingElse”的存在…除非a . cpp中有声明,比如:

// A.CPP
void doSomethingElse() ; // From B.CPP

void doSomething()
{
   doSomethingElse() ; // Defined in B.CPP
}

然后,如果你有使用相同符号的C.CPP,然后复制/粘贴声明…

复制/粘贴警报!

是的,有个问题。复制/粘贴是危险的,并且难以维护。这意味着如果我们有一些不复制/粘贴的方法,并且仍然声明符号将会很酷…我们该怎么做呢?通过包含一些文本文件,通常以.h, .hxx, .h++或,我更喜欢的c++文件,.hpp结尾:

// B.HPP (here, we decided to declare every symbol defined in B.CPP)
void doSomethingElse() ;

// A.CPP
#include "B.HPP"

void doSomething()
{
   doSomethingElse() ; // Defined in B.CPP
}

// B.CPP
#include "B.HPP"

void doSomethingElse()
{
   // Etc.
}

// C.CPP
#include "B.HPP"

void doSomethingAgain()
{
   doSomethingElse() ; // Defined in B.CPP
}

include是如何工作的?

从本质上讲,包含一个文件将解析并将其内容复制粘贴到CPP文件中。

例如,在下面的代码中,A.HPP报头:

// A.HPP
void someFunction();
void someOtherFunction();

... 来源B.CPP:

// B.CPP
#include "A.HPP"

void doSomething()
{
   // Etc.
}

... 纳入后将成为:

// B.CPP
void someFunction();
void someOtherFunction();

void doSomething()
{
   // Etc.
}

一件小事——为什么要把b.h ppp包括在b.c cpp中?

In the current case, this is not needed, and B.HPP has the doSomethingElse function declaration, and B.CPP has the doSomethingElse function definition (which is, by itself a declaration). But in a more general case, where B.HPP is used for declarations (and inline code), there could be no corresponding definition (for example, enums, plain structs, etc.), so the include could be needed if B.CPP uses those declaration from B.HPP. All in all, it is "good taste" for a source to include by default its header.

结论

因此,头文件是必要的,因为c++编译器不能单独搜索符号声明,因此,您必须通过包含这些声明来帮助它。

最后一句话:你应该在你的HPP文件的内容周围设置头保护,以确保多个包含不会破坏任何东西,但总而言之,我相信HPP文件存在的主要原因已经在上面解释过了。

#ifndef B_HPP_
#define B_HPP_

// The declarations in the B.hpp file

#endif // B_HPP_

或者更简单(尽管不是标准)

#pragma once

// The declarations in the B.hpp file

通常情况下,您希望有一个接口的定义,而不必交付整个代码。例如,如果您有一个共享库,您将附带一个头文件,该文件定义了共享库中使用的所有函数和符号。如果没有头文件,则需要发布源代码。

在一个项目中,头文件至少有两个用途:

清晰性,即通过保持接口与实现分离,更容易阅读代码 编译时间。通过尽可能只使用接口,而不是完整的实现,可以减少编译时间,因为编译器可以简单地对接口进行引用,而不必解析实际代码(理想情况下,只需要一次完成)。