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


当前回答

因为c++从C继承了它们。

其他回答

这是预处理器声明接口的方式。将接口(方法声明)放到头文件中,实现放到cpp中。使用您的库的应用程序只需要知道接口,它们可以通过#include访问该接口。

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

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

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

回应MadKeithV的回答,

这减少了依赖关系,因此使用标头的代码不会 必然需要知道实现的所有细节和任何 其他类/头只需要这样做。这将减少 编译次数,以及需要的重新编译量 实现中的某些内容发生了变化。

另一个原因是头文件为每个类提供了唯一的id。

如果我们有

class A {..};
class B : public A {...};

class C {
    include A.cpp;
    include B.cpp;
    .....
};

我们会有错误,当我们试图构建项目时,因为A是B的一部分,有了头文件,我们就可以避免这种头痛……

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

因为c++从C继承了它们。