为什么 C/C++ 中的函数体放在单独的源代码文件中而不是 headers?
Why are function bodies in C/C++ placed in separate source code files instead of headers?
例如,当我在 C++ 中定义一个 class 文件时,我总是将函数体与 class header 文件 (.h) 以及 class定义。源代码文件(.cpp)是带有 main() 函数的文件。现在这是在 pro c++ 程序员中普遍完成的,还是他们遵循单独的 header/source 代码文件的约定。
至于 Native C,我确实注意到在 GCC 中完成了(当然还有 Visual Studio 中 Windows 中的 headers)。
这只是约定俗成吗?或者这是有原因的吗?
函数体被放入 .cpp
文件以实现以下目的:
让编译器只解析和编译它们一次,而不是强制它再次编译它们,在包含 header 文件的任何地方一次又一次。此外,在 header 实现的情况下,链接器稍后必须检测并消除到达不同 object 文件的相同 external-linkage 函数。
许多现代编译器实现的 Header pre-compilation 设施可能会显着减少重复重新编译同一 header 文件所需的浪费工作,但它们并不能完全消除这个问题.
对模块或库的未来用户隐藏这些函数的实现。实现隐藏技术有助于强制执行某些编程规则,从而减少模块之间的寄生 inter-dependencies,从而导致更清晰的代码和更快的编译时间。
我什至会说,即使用户可以访问库的完整源代码(即没有任何东西是真正的 "hidden"),通过 [=52 可以看到的内容之间的清晰分离=] 文件和不应该可见的内容有利于库的 self-documenting 属性(尽管在 header-only 库中也可以实现这种分离)。
使某些功能"invisible"对外开放(即内部链接,与您使用class方法的示例不直接相关)。
Non-inline 驻留在特定翻译单元中的函数可以进行某些 context-dependent 优化。例如,具有相同尾部的两个不同函数可以最终 "sharing" 实现这些相同尾部的机器代码。
在 header 文件中声明为内联的函数在不同的翻译单元(即在不同的上下文中)被多次编译,并且稍后必须由链接器删除,这使得它变得更加困难(如果可能的话) ) 以利用此类优化机会。
我可能遗漏的其他原因。
这是约定俗成的,但也要看具体需求。例如,如果您正在编写一个您希望功能快速(内联)的库,并且您正在设计供其他人使用的库作为一个简单的 header only
库,那么您可以在header 个文件本身。
另一方面;如果您正在编写一个将静态或动态链接的库,并且您正在尝试封装来自用户的内部 object 数据。你的函数 - class 成员函数等将以一种方式编写,它们会做他们应该做的事情,这样你的库代码的用户就不必担心它的实际实现细节部分被隐藏。他们只需要了解您的功能,classes 就是他们的接口。通过这种方式,您将同时拥有 header 个文件和实现文件。
如果您将函数定义连同它们的声明一起放在 header 文件中,它们将是内联的并且应该 运行 更快,但是您的可执行文件会更大并且必须在每个文件中编译它们时间。实现细节也暴露给用户。
如果您将函数定义放在 header 的相关代码文件中,它们将不会内联,您的代码会更小,可能 运行 会慢一点,但您应该只必须编译一次。实现细节对用户是隐藏和抽象的。
在'c'中绝对没有理由将函数体放在头文件中。如果头文件包含在多个 'c' 文件中,这将迫使编译器多次定义函数。如果函数是'static',程序中会有多个副本,如果是全局的,链接器会报错。
类似的推理适用于 C++。 class 的 'inline' 成员和一些模板实现除外。
如果您在 'cpp' 文件中定义一个临时的 class,那么在那里定义它并在 class 中定义函数体是完全可以的。
例如,当我在 C++ 中定义一个 class 文件时,我总是将函数体与 class header 文件 (.h) 以及 class定义。源代码文件(.cpp)是带有 main() 函数的文件。现在这是在 pro c++ 程序员中普遍完成的,还是他们遵循单独的 header/source 代码文件的约定。
至于 Native C,我确实注意到在 GCC 中完成了(当然还有 Visual Studio 中 Windows 中的 headers)。
这只是约定俗成吗?或者这是有原因的吗?
函数体被放入 .cpp
文件以实现以下目的:
让编译器只解析和编译它们一次,而不是强制它再次编译它们,在包含 header 文件的任何地方一次又一次。此外,在 header 实现的情况下,链接器稍后必须检测并消除到达不同 object 文件的相同 external-linkage 函数。
许多现代编译器实现的Header pre-compilation 设施可能会显着减少重复重新编译同一 header 文件所需的浪费工作,但它们并不能完全消除这个问题.
对模块或库的未来用户隐藏这些函数的实现。实现隐藏技术有助于强制执行某些编程规则,从而减少模块之间的寄生 inter-dependencies,从而导致更清晰的代码和更快的编译时间。
我什至会说,即使用户可以访问库的完整源代码(即没有任何东西是真正的 "hidden"),通过 [=52 可以看到的内容之间的清晰分离=] 文件和不应该可见的内容有利于库的 self-documenting 属性(尽管在 header-only 库中也可以实现这种分离)。
使某些功能"invisible"对外开放(即内部链接,与您使用class方法的示例不直接相关)。
Non-inline 驻留在特定翻译单元中的函数可以进行某些 context-dependent 优化。例如,具有相同尾部的两个不同函数可以最终 "sharing" 实现这些相同尾部的机器代码。
在 header 文件中声明为内联的函数在不同的翻译单元(即在不同的上下文中)被多次编译,并且稍后必须由链接器删除,这使得它变得更加困难(如果可能的话) ) 以利用此类优化机会。
我可能遗漏的其他原因。
这是约定俗成的,但也要看具体需求。例如,如果您正在编写一个您希望功能快速(内联)的库,并且您正在设计供其他人使用的库作为一个简单的 header only
库,那么您可以在header 个文件本身。
另一方面;如果您正在编写一个将静态或动态链接的库,并且您正在尝试封装来自用户的内部 object 数据。你的函数 - class 成员函数等将以一种方式编写,它们会做他们应该做的事情,这样你的库代码的用户就不必担心它的实际实现细节部分被隐藏。他们只需要了解您的功能,classes 就是他们的接口。通过这种方式,您将同时拥有 header 个文件和实现文件。
如果您将函数定义连同它们的声明一起放在 header 文件中,它们将是内联的并且应该 运行 更快,但是您的可执行文件会更大并且必须在每个文件中编译它们时间。实现细节也暴露给用户。
如果您将函数定义放在 header 的相关代码文件中,它们将不会内联,您的代码会更小,可能 运行 会慢一点,但您应该只必须编译一次。实现细节对用户是隐藏和抽象的。
在'c'中绝对没有理由将函数体放在头文件中。如果头文件包含在多个 'c' 文件中,这将迫使编译器多次定义函数。如果函数是'static',程序中会有多个副本,如果是全局的,链接器会报错。
类似的推理适用于 C++。 class 的 'inline' 成员和一些模板实现除外。
如果您在 'cpp' 文件中定义一个临时的 class,那么在那里定义它并在 class 中定义函数体是完全可以的。