c++ - 为什么要有头文件和 .cpp 文件?

2025-05-18 01:25:32 世界杯瑞典 8745

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.CPP 无法知道“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.CPP 中包含 B.HPP?

在当前情况下,这不是必需的,B.HPP 有doSomethingElse函数声明,B.CPP 有doSomethingElse函数定义(它本身就是一个声明)。但在更一般的情况下,B.HPP 用于声明(和内联代码),可能没有相应的定义(例如,枚举、普通结构等),因此如果 B.CPP 可能需要包含使用 B.HPP 的那些声明。总而言之,默认情况下包含其标题的源是“好品味”。

结论

因此头文件是必要的,因为 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

岸边的意思、解释和含义
口袋妖怪游戏哪个好玩