什么应该进入.h 文件?

将代码分割成多个文件时,应该将什么内容放入。H 文件和应该进入。Cpp 文件?

115190 次浏览

您的类和函数声明加上文档,以及内联函数/方法的定义(尽管有些人喜欢将它们分开放置。Inl 档案)。

头文件(。H)应该用于类、结构及其方法、原型等的声明。这些对象的实现是在 cpp 中完成的。

进去

    class Foo {
int j;


Foo();
Foo(int)
void DoSomething();
}

我期望看到:

  • 声明
  • 评论
  • 标记为内联的定义
  • 模板

the really answer though is what not to put in:

  • 定义(可能导致事物被多重定义)
  • 使用声明/指令(将它们强加给任何人,包括您的头,可能导致名称冲突)

头文件(.h)设计用于提供多个文件中所需的信息。类声明、函数原型和枚举之类的东西通常放在头文件中。一句话,定义。

代码文件(.cpp)旨在提供只需要在一个文件中知道的实现信息。一般来说,函数体和内部变量应该/将永远不会被其他模块访问,它们属于 .cpp文件。简而言之,就是“实现”。

要确定什么属于哪里,最简单的问题是“如果我更改这个,我是否必须更改其他文件中的代码以使事情重新编译?”如果答案是“ yes”,那么它可能属于头文件; 如果答案是“ no”,那么它可能属于代码文件。

通常,您将声明放在头文件中,将定义放在实现中(。Cpp)档案。这种情况的例外是模板,其中的定义也必须放在标题中。

这个问题和类似的问题在 SO 上经常被问到,比如 为什么在 C + + 中有头文件和. cpp 文件?C + + 头文件,代码分离

标题(. h)

  • 宏,并包括接口所需的(尽可能少的)
  • 函数和类的声明
  • Documentation of the interface
  • 内联函数/方法的声明(如果有的话)
  • Externto 全局变量(如果有的话)

身体(. cpp)

  • 其他宏包括
  • 包含模块的头部
  • 函数和方法的定义
  • 全局变量(如果有的话)

根据经验,将模块的“共享”部分放在。H (其他模块需要能够看到的部分)和。心肺复苏术

PD: 是的,我已经包含了全局变量。我已经使用它们很多次了,重要的是不要在标题上定义它们,否则你会得到很多模块,每个模块都定义自己的变量。

Mainly header file contain 班级骨架 or 声明 (does not change frequently)

and cpp file contains 类实现 (changes frequently).

什么 编译成虚无(零二进制内存占用)进入头文件。

变量不会编译成任何东西,但类型声明可以(因为它们只描述变量的行为)。

函数不会,但内联函数会(或宏) ,因为它们只在调用。

模板不是代码,它们只是创建代码的配方。

除了所有其他的答案,我会告诉你什么是你不能放在一个头文件:
using声明(最常见的是 using namespace std;)不应该出现在头文件中,因为它们会污染包含它的源文件的命名空间。

标题 定义什么的,但是没有告诉任何关于实现的内容。(不包括模板在这个“图元”中。

也就是说,您需要将“定义”划分为子组,在本例中有两种类型的定义。

  • 您定义了结构的“布局”,只说明周围使用组所需要的内容。
  • 变量、函数和类的定义。

现在,我说的当然是第一个子群。

报头是用来定义结构的布局,以便帮助软件的其他部分使用实现。您可能希望将其视为您的实现的“抽象”,虽然这样说有些唐突,但是,我认为它在这种情况下非常适合。

正如之前的海报所说,并显示您声明了私有和公共使用区域及其标题,这也包括私有和公共变量。现在,我不想在这里讨论代码的设计,但是,您可能需要考虑您在头中放入了什么,因为那是最终用户和实现之间的层。

  • 头文件-不应该在开发过程中经常更改-> 您应该思考,并立即编写它们(在理想情况下)
  • 源文件-实现过程中的更改

事实上,在 C + + 中,这比 C 头/源组织要复杂一些。

编译器会看到什么?

编译器看到一个大的源(。Cpp)文件,并正确包含其标头。源文件是将被编译成目标文件的编译单元。

So, why are headers necessary?

因为一个编译单元可能需要关于另一个编译单元中的实现的信息。例如,可以在一个源中编写函数的实现,并在需要使用该函数的另一个源中编写该函数的声明。

在这种情况下,有两个副本的相同的信息。这是邪恶的..。

解决方案是分享一些细节。虽然实现应该保留在 Source 中,但可能需要共享共享符号的声明,如函数或结构、类、枚举等的定义。

Headers are used to put those shared details.

将需要在多个源之间共享的声明移到标题

Nothing more?

In C++, there are some other things that could be put in the header because, they need, too, be shared:

  • 内联代码
  • 模板
  • 常量(通常是那些您想要使用的内部开关...)

将所有需要共享的内容移到头部,包括共享实现

Does it then mean that there could be sources inside the headers?

是的。事实上,有很多不同的东西可能在一个“头”(即源之间共享)。

  • 转发声明
  • 函数/结构/类/模板的声明/定义
  • 内联代码和模板代码的实现

它变得复杂,在某些情况下(符号之间的循环依赖) ,不可能保持在一个标题。

标题可以分为三个部分

这意味着,在极端情况下,你可以:

  • 一个前向声明
  • 声明/定义标头
  • 实现标头
  • an implementation source

假设我们有一个模板化的 MyObject,我们可以:

// - - - - MyObject_forward.hpp - - - -
// This header is included by the code which need to know MyObject
// does exist, but nothing more.
template<typename T>
class MyObject ;

.

// - - - - MyObject_declaration.hpp - - - -
// This header is included by the code which need to know how
// MyObject is defined, but nothing more.
#include <MyObject_forward.hpp>


template<typename T>
class MyObject
{
public :
MyObject() ;
// Etc.
} ;


void doSomething() ;

.

// - - - - MyObject_implementation.hpp - - - -
// This header is included by the code which need to see
// the implementation of the methods/functions of MyObject,
// but nothing more.
#include <MyObject_declaration.hpp>


template<typename T>
MyObject<T>::MyObject()
{
doSomething() ;
}


// etc.

.

// - - - - MyObject_source.cpp - - - -
// This source will have implementation that does not need to
// be shared, which, for templated code, usually means nothing...
#include <MyObject_implementation.hpp>


void doSomething()
{
// etc.
} ;


// etc.

哇!

在“现实生活”中,情况通常没那么复杂。大多数代码只有一个简单的头/源组织,源代码中有一些内联代码。

但在其他情况下(模板化对象彼此认识) ,我必须为每个对象分别使用声明和实现标头,使用包含这些标头的空源来帮助我查看一些编译错误。

将标题分解为单独的标题的另一个原因可能是为了加快编译速度,限制符号解析到严格必要的数量,并避免不必要的重新编译源代码,因为当内联方法实现发生变化时,源代码只关心前向声明。

结论

您应该使您的代码组织尽可能简单,并且尽可能模块化。在源文件中放入尽可能多的内容。只在标题中显示需要共享的内容。

但是有一天,您将在模板化对象之间建立循环依赖关系,如果您的代码组织变得比普通的头/源组织更“有趣”,请不要感到惊讶..。

^ _ ^