将您的代码分成多个文件时,.h文件中到底应该放什么,.cpp文件中应该放什么?
.hpp
文件中,而C声明放入.h
文件中。这在混合C和C ++代码(例如C中的旧模块)时非常有用。
将您的代码分成多个文件时,.h文件中到底应该放什么,.cpp文件中应该放什么?
.hpp
文件中,而C声明放入.h
文件中。这在混合C和C ++代码(例如C中的旧模块)时非常有用。
Answers:
头文件(.h
)旨在提供多个文件中所需的信息。诸如类声明,函数原型和枚举之类的东西通常放在头文件中。一句话,就是“定义”。
代码文件(.cpp
)用于提供只需要在一个文件中知道的实现信息。通常,文件中应该包含/不应该被其他模块访问的函数主体和内部变量.cpp
。一句话,“实现”。
问自己确定属于哪个问题的最简单的问题是:“如果更改此内容,是否必须更改其他文件中的代码以使事情再次编译?” 如果答案为“是”,则它可能属于头文件;如果答案为“否”,则它可能属于代码文件。
export
)。围绕#1的唯一方法是PIMPL。如果export
受支持,则可能会#2,并且可能会使用c ++ 0x和extern
模板。IMO,c ++中的头文件失去了很多用处。
实际上,在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.
在“现实生活”中,它通常不那么复杂。大多数代码只有一个简单的标头/源代码组织,源代码中带有一些内联代码。
但是在其他情况下(模板对象彼此了解),我必须为每个对象分别使用声明和实现标头,并且包括这些标头的空源只是为了帮助我看到一些编译错误。
将标头分解为单独的标头的另一个原因可能是加快编译速度,将符号解析的数量限制在严格必要的范围内,以及避免在内联方法实现发生更改时仅关心正向声明的源的不必要的重新编译。
您应该使代码组织尽可能简单,并且尽可能模块化。尽可能将其放在源文件中。仅在标头中公开需要共享的内容。
但是有一天,模板对象之间会产生循环依赖关系,如果您的代码组织变得比普通标头/源组织更“有趣”,也不要感到惊讶。
^ _ ^
除了所有其他答案外,我还会告诉您不要放在头文件中的内容:
using
声明(最常见的是using namespace std;
)不应出现在头文件中,因为它们会污染包含它的源文件的名称空间。
using
将标头中的内容带入全局名称空间。
static inline
在C99中使用的情况,建议在C ++中使用匿名命名空间中的内联函数,因为这与将内部链接与模板结合在一起时会发生什么情况有关。Anon名称空间使您可以“隐藏”函数,同时保留外部链接。
什么编译成什么(零二进制足迹)进入头文件。
变量不会编译为空,但是类型声明会编译(因为它们仅描述变量的行为)。
函数不会,但是内联函数(或宏)会这样做,因为它们仅在被调用的地方生成代码。
模板不是代码,它们只是创建代码的秘诀。因此它们也放入h文件中。
通常,将声明放在头文件中,将定义放在实现(.cpp)文件中。模板是一个例外,模板中的定义也必须放在标头中。
在SO上经常问到这个问题和与之类似的问题-请参阅为什么C ++中有头文件和.cpp文件?和C ++头文件,例如代码分隔。
主要头文件包含类框架或声明(不经常更改)
并且cpp文件包含类的实现(经常更改)。
我希望看到:
真正的答案是不应该输入什么:
标头定义一些内容,但未说明任何有关实现的内容。(不包括此“ metafore”中的模板。
话虽如此,您需要将“定义”分为多个子组,在这种情况下,有两种类型的定义。
现在,我当然是在谈论第一个小组。
标头用于定义结构的布局,以帮助其余软件使用该实现。您可能希望将其视为实现的“摘要”,这是不客气的说,但是,我认为这种情况非常适合。
如前所述,并显示您声明了私有和公共使用区域及其标题,其中还包括私有和公共变量。现在,我不想在这里进行代码设计,但是,您可能要考虑您在标头中放置的内容,因为这是最终用户和实现之间的层。
标头(.h)
正文(.cpp)
根据经验,您可以将模块的“共享”部分放在.h(其他模块需要能够看到的部分)上,而将“非共享”部分放在.cpp上
PD:是的,我包括了全局变量。我已经使用了它们几次,重要的是不要在标题上定义它们,否则您将获得很多模块,每个模块都定义自己的变量。
编辑:大卫的评论后修改