.h
文件中,实现则在.cpp
文件中。然而,最近我尝试了一段时间的C#,我非常喜欢它清晰的语法和文件组织方式,特别是没有头文件和实现文件之分,你通常为每个.cs
文件实现一个类,而且不需要头文件。我知道在C++中这也是可能的(你可以在
.h
文件中编写"inline"函数),但到目前为止,在C++项目中,我总是看到明确的.h
和.cpp
文件之间的区别。这种方法的优缺点是什么呢?谢谢。
.h
文件中,实现则在.cpp
文件中。然而,最近我尝试了一段时间的C#,我非常喜欢它清晰的语法和文件组织方式,特别是没有头文件和实现文件之分,你通常为每个.cs
文件实现一个类,而且不需要头文件。.h
文件中编写"inline"函数),但到目前为止,在C++项目中,我总是看到明确的.h
和.cpp
文件之间的区别。这种方法的优缺点是什么呢?在C++中,将接口和实现分开有几种好处。首先,如果您想要更新一个库而不更改接口,则在C++文件中编写代码意味着您只需要更新该库,而不是库加上头文件。其次,它隐藏了实现细节。也就是说,它强制人们仅从接口的角度来查看您的类,这是应该关注的事情,如果代码编写得很好的话。最后,将接口和文档分离具有某种美学上的清晰度。尽管您可能需要一些时间来适应这种方式,但在一段时间后,它会变得自然(个人观点)。
不要忘记构建时间。
将实现代码放在头文件中会使其更容易被更改。而更改头文件将导致包含它们的所有CPP文件重新构建,从而增加构建时间。在大型项目中,这可能是相当重要的。
我也喜欢将实现隐藏在我的库的用户之外。不幸的是,这对于模板类不起作用。
我的经验法则:将声明放在.H文件中,将定义放在.CPP文件中。
如果你想要将C ++与已编译的二进制文件(通常使用库时)合并,那么在一个地方定义符号会更加方便。想象一下,如果你需要为二进制文件中的全局内容定义外部符号。如果你在同一个文件中有.cpp和.h代码,那么你必须为每个这样的文件定义二进制文件的符号。而如果你采用两个文件的方式,你只需有一个.h文件来定义二进制文件的符号,然后有很多.cpp文件来使用它。
.h
文件中的话,它会被放置在包含该头文件的每个编译单元中,这样在编译阶段会产生冗余,而使用.h
和.cpp
分割的方式会将其编译为一个单独的目标文件,然后通过链接与其他目标文件一起使用,从而只生成一个已实现该头文件的编译二进制代码。.h
文件中声明变量和结构体,那么你将无法在其他.cpp
文件之间共享它们。有趣的是,最近 C# 在某种程度上似乎朝着 C/C++ 的方向发展,引入了部分类。
在 IDE 中,这样做的特殊优势是 Visual Studio 设计器将修改处理可视控件或数据成员及其布局的类部分,而不必担心会搞乱存储在单独文件中的方法(应用程序逻辑)。
我想回应@wheaties并补充一些内容
编译更容易(也许只是我),如果你仅修改头文件(如所有已包含它的实现文件),我从未能够使编译正常工作。我相信在Makefiles中,您必须手动添加依赖项,这对于非常大规模的项目来说是真正痛苦的事情(可能只是我)。因此,如果您的代码在实现文件中,则更改仅意味着重新编译该特定文件-当您想要进行快速更改、构建和测试时非常有用。
让我重申隐藏方面,由于代码的敏感性质,通常您不希望人们知道实现细节,因此仅公开头文件加预构建库,分离在这里是关键。
前向声明是一个巧妙的技巧,如果在头文件中没有任何代码“使用”类的实现细节,那么您不需要在头文件中包含它,但是在实现文件中,您可以包含真正的头文件,“它都可以很好地工作”(如果您有循环依赖关系,则有所帮助-为什么您有它们是不同的问题!)
我看到很多回复都主张分离,主要是为了构建时和实现隐藏的好处。这两点肯定是优点,但我会举出反例:Boost。
大多数Boost库使用没有外部链接的.hpp
文件。原因是在模板的情况下通常需要这样做,当编译器必须从调用程序中知道参数类型时。因此,如果您想坚持放弃类而选择模板的“现代”C++方法,可能就没有选择了。
.cs
和 .cpp
/.h
的部分,我认为你需要记住 C# 的首席架构师 Anders Hejlsberg 的背景。在 Delphi 中,您也没有头文件和模块的区别(忽略包含文件以进行讨论)。您只需在单元文件中有两个部分 initialization
和 implementation
。