在.cpp和.h文件中定义函数方法的区别

6
为了减少工作中一个相当大的框架的编译时间,我考虑将.h文件中的类方法定义移到它们相关的.cpp文件中,如果它们非常大或需要包含可以移动到相关的.cpp文件中进行编译。为了清晰起见,下面是一个人为构造的例子(虽然Foo::inc是一个微小的方法)。
main.cpp:
#include "Foo.h"

int main(int argc, char** argv) {
    Foo foo(argc);
    foo.inc();
    return foo.m_argc;
}

Foo.h在(尚不需要Foo.cpp)之前:

class Foo {
public:
    int m_argc;
    Foo (int argc) : m_argc(argc) {}
    void inc() { m_argc++; }
};

Foo.h修改后的内容:

class Foo {
public:
    int m_argc;
    Foo (int argc) : m_argc(argc) {}
    void inc();
};

Foo.cpp:

#include "Foo.h"

void Foo::inc() { m_argc++; }

很久以前,我的一位同事曾提到在某些情况下,这可能会导致运行时性能减慢。我在Google上寻找这种情况,但似乎找不到。这个问题的被接受答案是我能找到的最接近的答案,但它并没有给出具体情况,只是提到可能发生:将内联方法从头文件移到.cpp文件中
另外,我不感兴趣的是一个方法明确使用了inline的情况,上面链接中的答案只是我能找到的最接近我所寻找的内容。
有哪些情况(如果有)会导致运行时减速?

3
你的例子可能会导致减速。在当前代码中,“inc”函数是内联和非虚拟定义的,因此编译器可以内联该函数而无需函数调用开销。新版本需要完整的函数调用。如果“inc”函数经常被使用,则内联函数可以代表相当大的节省。如果很少使用,则开销无关紧要。 - Jonathan Leffler
1
@JonathanLeffler,谢谢您,我没有意识到这已经是我正在寻找的情况。我知道在原始实现中是否进行内联取决于编译器,但是否有一种方法可以保留编译器在移动版本中的决策呢? - asimes
1
(仅供参考)据我所知,跨模块链接时间优化需要使用-flto。如果没有它,第二种情况将涉及函数调用,因此速度会变慢。然而,-flto也有一些副作用(可能会增加文件大小)。 - asimes
只想添加两个好的参考资料:链接器能够内联函数吗? 和一个更一般的讨论关于C++中内联函数的好处? - Florian
2个回答

7

你原来的方法 void inc() { m_argc++; } 被自动标记为 inline,所以编译器可以用内联版本取代函数调用。

一旦你将该方法从类定义中移出到模块中,该方法就不再是inline的了,内联扩展不会发生,使用标准的函数调用,结果可能会变慢。


1
为什么不会内联??编译器无法检测到该方法如此琐碎,如果它在不同的文件中,则应内联吗?? - DGomez
3
模块是独立编译的。为了使函数/方法内联,其源代码必须可用,因此通常必须在头文件中存在。 - dlask
1
如果你使用的是1995年的编译器或者某些晦涩的嵌入式环境下设计的编译器,这可能是真的。但是现在所有主流的编译器都支持某种形式的链接时代码生成和优化,并且能够内联其他翻译单元中定义的函数。 - bcrist
2
阅读了一些资料后,似乎我需要使用-flto进行跨模块链接时间优化,因此我认为dlask的答案是正确的。 -flto有一些我不喜欢的副作用,所以我想答案是肯定的,第二种情况会更慢。 - asimes
1
这个答案完全是错误的。任何相当现代(<10年)的编译器都具有整个模块优化功能。 - The Paramagnetic Croissant
显示剩余4条评论

2
减少头文件依赖是减少编译时间的好方法。这是像如何加速C++编译时间?这样的列表中排名前几的项目之一。
我建议 - 如果还没有这样做 - 查看使用C++编译过程分析工具消耗大量编译时间的主要因素。
还有辅助工具可以帮助您整理包含的依赖关系,请参阅自动化C++ #include重构
关于将代码移动到源文件是否会降低运行时性能的问题:这取决于情况。一般来说,如果你在头文件中有函数,你可以让编译器有机会进行内联。
我喜欢引用C++ FAQ - 内联中的话。
内联函数能提高性能吗?
有时候可以,有时候不行,也许吧。
并没有简单的答案。内联函数可能会使代码更快,也可能会使其更慢。它们可能会使可执行文件变大,也可能会使其变小。它们可能会导致抖动,也可能会防止抖动。而且它们可能是,而且经常是,与速度完全无关的。
编译器(也许稍后是链接器)对其的处理取决于您使用的编译器工具链以及您给出的编译器/链接器选项。
例如,请参见inline - Using the GNU Compiler Collection中的所有发生情况:
... 当未进行优化时,GCC 不会内联任何函数 ...
一些参考资料:

基本上这些都与问题无关,您可以根据顶部提供的LTO链接直接做出答复。 - asimes
@asimes 你说得对。我被“我的同事说它会降低运行时性能”这句话分散了注意力。我缩小了回答的范围,并添加了一些关于减少C++编译时间的内容,因为我认为你可以节省将代码移动到.cpp文件中的工作量。 - Florian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接