我见过将类的代码放入单独的 C++ 中,而方法定义则放在头文件中。我的第一个 OOP 经验是使用 Java,其中所有方法都放在类文件中,实际上我更喜欢这样。
将我的所有方法放在头文件中是否会影响编译器生成的汇编代码?
如果是这样,将类的整个代码放在其头文件中是否会对性能产生不利影响?
重点是,复杂的 C++ 程序是通过编译多个对象,然后将它们链接在一起来创建的。 每个对象通常是编译一个实现文件(例如“.cpp”、“.cc”等)的结果,该文件可能直接或间接包含许多标头。 因此,如果您编写一个好的类并将代码放入标头中,那么该代码可以包含在多个目标文件中,然后编译器会冗余地生成它,并且进一步 - 链接器不会(并且不能轻易)进行比较版本以查看它们是否等效并删除冗余副本(如果使用相对地址更容易 - “位置无关代码” - 但那是另一个故事)。 另请参阅下面 jalf 的评论。
因此,您不希望标头中出现不同的外线函数。 如果它们名义上是
inline
函数(由于使用了 inline 关键字或在类中定义),那么编译器只需承担额外的工作并确保它们的任何外联版本都是唯一表示的在可执行文件中。 但是,对于超出范围的函数,负担仍然由程序员承担。
此外,如果您在标头中提供实现,则会为每个对象进行冗余编译,并且对标头的任何更改都将强制重新编译所有相关对象。 可以更改单独对象中的外线函数,重新编译该单个对象,然后将其与其他预先存在的对象链接以形成新的可执行文件。 在大型项目中,这可以节省大量编译时间。
头文件/实现拆分有几个有效的原因
单独编译:
1.这可能是工作要求 - 例如,您要提供
二进制库+头文件给某人,或者你的同事太保守了
接受其他任何东西。
2.它仍然需要开发非常大的项目(例如,> 10M 的源代码),
因为每次修改后重建整个应用程序会变得很痛苦。
(但是将 jpeglib 或 zlib 之类的东西编译为单个模块应该还是可以的)
3.有一种观点认为使用头文件作为参考更容易查看
等功能。 (但通常最好编写适当的文档;与标题不同,文档中的错误不太可能影响您的程序)
此外,还有更多不再使用它的理由:
1.您希望避免维护重复的代码。
2.类方法不需要前向声明
3.无论如何,模板只能在标头中声明
4.不需要函数内联的情况实际上相当罕见,
即在紧密循环中多次调用大函数,但是有
noinline 属性和 PGO。否则内联可以提高速度。
至于代码膨胀,无论如何,大多数库已经很大了。
5.总体而言,作为单一源编译的程序更快、更小,
因为编译器可以做得更好。
6.如果没有标头,源文件通常会小两倍左右,
并且编译器将能够正确检查语法,因此您将无法
意外地将 extern "C" cdecl 函数原型链接到变量作为实现。
总的来说,它会更加可移植,因为不同的链接器对于名称匹配有不同的想法。
7.这很奇怪,但动态分配被频繁使用只是因为
标头样式 - 可以通过定义所有的来自动解决依赖关系
单个类中的详细信息,但人们更喜欢使用指向部分类声明的指针(然后寻找内存泄漏)。
现在,单独的对象模块有一些奖励点:
4. gcc 中的 PGO 统计信息是按对象模块生成的,这似乎是使用单个可执行文件对几种不同操作模式进行“基准测试”的唯一方法。
5.可以使用不同的编译器选项编译不同的模块以优化速度。还有一些编译器扩展,但它们不是很可靠。
6.有时,当您修改某些内容时,编译器可能会对代码的另一部分执行一些奇怪的操作 - 但通常它不能传播到对象模块之外。
是的,放置在标头中的方法是内联的,因此它们通常更快(尤其是短方法)。
主要缺点是标头中的每次修改都会导致包含它的每个文件的重新编译。