我一直在研究 Boost 和其他各种 C++ 库。Boost 的绝大部分是在头文件中实现的。
我的问题是:在什么条件下,您会执行仅标头实现(如 Boost)还是包含 .cpp 文件?
我一直在研究 Boost 和其他各种 C++ 库。Boost 的绝大部分是在头文件中实现的。
我的问题是:在什么条件下,您会执行仅标头实现(如 Boost)还是包含 .cpp 文件?
如果您想在另一个翻译单元(即另一个源文件)中使用模板,您应该(几乎总是)在头文件中定义它。(也有例外,就像下面的评论指出的那样,但恕我直言,这是一个很好的经验法则。)
如果您想使用来自另一个翻译单元的内联函数,同样适用。
否则,您应该将实现放入单独的 .cpp 文件中以最小化依赖关系。
理解这个问题基本上就是理解 C++ 编译单元是如何工作的。#include
头文件里的东西基本上是通过语句粘贴到一大堆编译单元的源代码中的。每个编译单元都被编译成一个目标文件,目标文件被链接起来,你会因为这些东西被复制到各处而产生冲突。
例外是(至少从历史上看)不进入目标文件的东西,因为编译器直接处理它们(例如内联函数),以及不能在一个单元中编译然后链接到另一个单元的东西,因为它们是'没有完全定义(模板)。模板函数通常在多个编译单元中被相同地实例化,并且链接器具有特殊的智能来丢弃重复项。
这意味着接口和实现到头文件和正文文件的分离不是很干净。Ada 有一个更清晰的分离 - 但一个更复杂的构建过程来补偿 IIRC。Java 只是删除了接口和实现的单独文件。
多年来,链接器变得更加复杂,并接管了编译器的一些工作,如今很多这种解释都是错误的,但基本模式仍然存在。模板函数和内联函数可以(并且经常需要)与您的所有不直接生成对象代码共享声明一起进入标题。普通函数定义不应位于头文件中。