什么应该进入.h文件?

时间:2009-12-22 11:38:43

标签: c++ header-files

将代码分成多个文件时,究竟应该将哪些内容放入.h文件中以及应该放入.cpp文件中的内容?

12 个答案:

答案 0 :(得分:97)

标头文件(.h)旨在提供多个文件中所需的信息。类声明,函数原型和枚举等内容通常都在头文件中。总之,“定义”。

代码文件(.cpp)旨在提供只需要在一个文件中知道的实现信息。通常,函数体和其他模块应该/永远不会访问的内部变量属于.cpp文件。总之,“实施”。

要问自己确定哪些属于哪里的最简单的问题是“如果我更改了这个,我是否必须更改其他文件中的代码才能重新编译?”如果答案是“是”,它可能属于头文件;如果答案是“否”,它可能属于代码文件。

答案 1 :(得分:49)

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

编译器看到了什么?

编译器会看到一个大的源(.cpp)文件及其正确包含的标头。源文件是将编译为目标文件的编译单元。

那么,为什么标题必要?

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

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

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

标题用于放置这些共享详细信息。

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

仅此而已?

在C ++中,还有一些其他内容可以放在标题中,因为它们也需要共享:

  • 内联代码
  • 模板
  • 常量(通常是你想在开关中使用的那些......)

转移到标题所有需要共享的内容,包括共享实现

那么它是否意味着标题中可能有源?

是。实际上,有很多不同的东西可以在“标题”内(即在源之间共享)。

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

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

标题可以分为三个部分

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

  • 前向声明标题
  • 声明/定义标题
  • 实施标题
  • 实施源

让我们假设我们有一个模板化的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.

哇!

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

但是在其他情况下(模板对象知道彼此),我必须为每个对象分别设置声明和实现标头,包含这些标头的空源只是为了帮助我看到一些编译错误。

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

结论

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

但是当你在模板化对象之间存在循环依赖时,如果你的代码组织变得比普通的头/源组织更“有趣”,那就不要感到惊讶......

^ _ ^

答案 2 :(得分:15)

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

答案 3 :(得分:5)

什么编译成什么(零二进制占用空间)进入头文件。

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

函数没有,但内联函数做(或宏),因为它们只在被调用的地方生成代码。

模板不是代码,它们只是创建代码的秘诀。所以他们也进入h档。

答案 4 :(得分:3)

通常,您将声明放在头文件中,并在实现(.cpp)文件中定义。例外情况是模板,其中定义也必须放在标题中。

此问题和类似的问题经常被问到 - 例如Why have header files and .cpp files in C++?C++ Header Files, Code Separation

答案 5 :(得分:1)

您的类和函数声明以及文档和内联函数/方法的定义(尽管有些人更喜欢将它们放在单独的.inl文件中)。

答案 6 :(得分:1)

主要标题文件包含类骨架声明(不经常更改)

和cpp文件包含类实现(经常更改)。

答案 7 :(得分:0)

头文件(.h)应该用于声明类,结构及其方法,原型等。这些对象的实现是在cpp中进行的。

in .h

    class Foo {
    int j;

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

答案 8 :(得分:0)

我希望看到:

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

真正的答案是不应该放入的内容:

  • 定义(可以导致事物被多重定义)
  • 使用声明/指令(强制它们包括你的标题在内的任何人,都可能导致名字冲突)

答案 9 :(得分:0)

标题(.h)

  • 接口所需的宏和包含(尽可能少)
  • 函数和类的声明
  • 界面文档
  • 内联函数/方法的声明,如果有的话
  • extern to global variables(if any)

身体(.cpp)

  • 其余的宏,包括
  • 包含模块的标题
  • 功能和方法的定义
  • 全局变量(如果有)

根据经验,您将模块的“共享”部分放在.h(其他模块需要能够看到的部分)和.cpp上的“未共享”部分

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

编辑:在David的评论之后修改

答案 10 :(得分:0)

标题定义,但不会告诉任何有关实现的内容。 (不包括此“metafore”中的模板。

话虽如此,你需要将“定义”划分为子组,在这种情况下,有两种类型的定义。

  • 您可以定义结构的“布局”,仅告知周围使用组所需的内容。
  • 变量,函数和类的定义。

现在,我当然在谈论第一个小组。

标题用于定义结构的布局,以帮助其余软件使用实现。你可能希望将它看作是你实现的“抽象”,这是有道理的,但我认为在这种情况下它非常适合。

正如之前的海报所述,并显示您声明私人和公共使用区域及其标题,这也包括私人和公共变量。现在,我不想在这里设计代码,但是,您可能想要考虑在头文件中放置的内容,因为这是最终用户和实现之间的层。

答案 11 :(得分:0)

  • 标题文件 - 在开发过程中不应经常更改 - &gt;你应该思考,并立即写下(理想情况下)
  • 源文件 - 实施期间的更改