什么应该进入一个.h文件?
当你的代码分成多个文件,究竟应该进入一个.h文件,什么应该进入.cpp文件?
头文件( .h
)旨在提供将在多个文件中需要的信息。 像类声明,函数原型和枚举的东西通常在头文件。 总之,“定义”。
代码文件( .cpp
)旨在提供只需要在一个文件中知道的实现信息。 一般来说,函数体和其他模块不应该访问的内部variables属于.cpp
文件。 总之,“实现”。
最简单的问题是要问自己,以确定什么属于哪里“如果我改变这个,我将不得不改变其他文件中的代码,使事情再次编译? 如果答案是“是”,则可能属于头文件; 如果答案是“否”,则可能属于代码文件。
事实上,在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.
哇!
在“现实生活”中,通常较为复杂。 大多数代码将只有一个简单的头/源组织,在源代码中有一些内联代码。
但在其他情况下(模板化的对象彼此了解),我必须为每个对象分别声明和实现标题,包含这些标题的空白源只是为了帮助我看到一些编译错误。
将头文件分解成单独头文件的另一个原因是可以加快编译速度,将parsing符号的数量限制在严格必要的范围内,并避免在内联方法实现发生变化时只关心前向声明的源程序的不必要的重新编译。
结论
你应该使你的代码组织尽可能简单,尽可能地模块化。 把尽可能多的源文件。 只在头文件中显示需要共享的内容。
但是,当模板对象之间有循环依赖的时候,如果你的代码组织变得比平常的标题/源代码组织更“有趣”,不要感到惊讶。
^ _ ^
除了所有其他的答案,我会告诉你你不放在一个头文件:
using
声明(最常见的是using namespace std;
)不应该出现在头文件中,因为它们会污染它所在的源文件的名称空间。
什么编译成零(零二进制足迹)进入头文件。
variables不会编译成任何东西,但是types声明可以做(因为它们只描述variables的行为)。
函数不行,但内联函数做(或macros),因为它们只在被调用的地方产生代码。
模板不是代码,它们只是创build代码的一个配方。 所以他们也去h文件。
通常,将声明放入实现(.cpp)文件中的头文件和定义中。 这个例外是模板,其中的定义也必须在标题中。
这个问题和类似的问题已经经常被问到 – 请参阅为什么在C ++中有头文件和.cpp文件? 和C ++头文件,例如代码分离 。
你的类和函数声明加上文档,以及内联函数/方法的定义(尽pipe有些人喜欢把它们放在单独的.inl文件中)。
主要的头文件包含类骨架或声明 (不频繁更改)
和cpp文件包含类实现 (频繁更改)。
头文件(.h)应该用于类,结构体及其方法,原型等的声明。这些对象的实现是在cpp中完成的。
在.h
class Foo { int j; Foo(); Foo(int) void DoSomething(); }
我希望看到:
- 声明
- 注释
- 定义标记为内联
- 模板
真正的答案虽然是不能放在:
- 定义(可以导致事物被多重定义)
- 使用声明/指令(强制他们包括你的头,可以导致nameclashes)
标题定义了一些东西,但是并没有告诉任何有关实现的内容。 (不包括此“metafore”中的模板。
有了这个说法,你需要把“定义”划分成小组,在这种情况下,有两种types的定义。
- 你可以定义你的结构的“布局”,只是尽可能的告诉周围的用户组。
- variables,函数和类的定义。
现在,我当然在谈论第一个小组。
标题在那里定义你的结构的布局,以帮助软件的其余部分使用实现。 你可能想把它看作是你的实现的“抽象”,这是虚构的说法,但我认为在这种情况下非常合适。
正如以前的海报所说,并显示你声明私人和公共使用区域及其标题,这还包括私人和公共variables。 现在,我不想进入这里的代码devise,但是,你可能要考虑你放在你的头文件,因为这是最终用户和实现之间的层。
标题(.h)
- 所需的macros和接口(尽可能less)
- function和类的声明
- 界面的文档
- 内联函数/方法的声明,如果有的话
- 外部到全局variables(如果有的话)
Body(.cpp)
- 其余的macros和包括
- 包含模块的标题
- function和方法的定义
- 全局variables(如果有的话)
根据经验,将模块的“共享”部分放在.h(其他模块需要看到的部分)和.cpp上的“not shared”部分
PD:是的,我已经包含全局variables。 我已经使用过它们了,重要的是不要在头文件中定义它们,否则会得到很多模块,每个模块定义自己的variables。
编辑:修改后的大卫的评论
- 头文件 – 不应该在开发过程中经常更改 – >你应该思考,并立即写(在理想的情况下)
- 源文件 – 实施过程中的变化