前向声明如何节省编译时间



如果你在网上阅读,那么有很多人声称,如果你使用前向声明,那么C++它会节省你的编译时间。通常的理论是,如果我使用前向声明,#include意味着仅仅是文本替换,那么我的编译器不需要解析标头并可能编译它,因此可以节省时间。我发现这种说法很难相信,因为考虑到我通常看到这样的代码:

// B.h
class A;
class B {
public:
void doSomething(A& a);
}

在这种情况下,是的,我们不需要在转发声明时将A.h包含在B.h中,但问题是,最终B.cpp,我们需要一个完整的类型A来使用它的方法和数据成员。所以我发现在几乎所有情况下,我们都需要在B.cpp中包含A.h

那么前向声明如何实际节省编译时间呢?我看到有人用基准来证明,如果他们使用前向声明而不是#includes,编译时间实际上会下降,所以这里一定有什么我不明白的地方......


我知道节省编译时间不是前向声明的唯一目的,我知道它还有其他目的。我只是想了解为什么有些人声称它可以节省编译时间。

编译时间

问题在于,最终B.cpp,我们需要一个完整的类型A来使用它的方法和数据成员。

是的,这是一个典型的模式。前向声明一个类(例如A) 在标头中(例如B.h),然后在对应于该标头 (B.cpp) 的源代码中,包括前向声明类 (A.h) 的标头。

所以我发现在几乎所有情况下,我们都需要在B.cpp中包含B.h

正确的前向声明在编译相应的源代码时不会节省时间。编译其他使用B的源代码时,可以节省成本。例如:

其他.cpp

#include "B.h"
// Do stuff with `B` objects.
// Make no use of `A` objects.

假设此文件不需要A.h中的定义。这就是节省的地方。在编译other.cpp时,如果B.h使用A的前向声明,则不需要处理A.h。也不需要处理A.h本身包含的标头,等等。现在将此效果乘以包含B.h的文件数,直接或间接地。

请注意,这里有复合效应。"A.h本身包含的标头"和"包含B.h的文件"的数量将是用前向声明替换任何#include语句之前的数字。(一旦你开始进行这些替换,数字就会下降。

效果有多大?不像以前那么多了。尽管如此,只要我们在理论上谈论,即使是最小的储蓄仍然是储蓄。

重建时间

我认为比原始编译时间(构建所有内容)更关注的是重建时间。也就是说,仅编译受所做更改影响的文件所需的时间。

假设有十个文件依赖于B.h但不依赖于A.h。如果B.h包含A.h,那么这十个文件将受到A.h更改的影响。如果B.h转发声明A,那么这些文件不会受到A.h更改的影响,从而减少了在这些更改后重建的时间。

现在假设有另一个类,称之为B2,它也可以选择转发声明A而不是包含标头。也许还有另外十个文件依赖于B2但不依赖于B,也不依赖于A。现在有二十个文件在更改A后不需要重新编译。

但为什么要止步于此呢?让我们将B3B10添加到组合中。现在有一百个文件在更改A后不需要重新编译。

添加另一个图层。假设有一个C.h可以选择转发声明B而不是包含B.h。通过使用前向声明,对A.h的更改不再需要重新编译使用C.h的十个文件。而且,当然,我们假设每个BB10都有十个这样的文件。现在,我们最多10*10*10个文件,这些文件在A.h更改时不需要重新编译。

带走

这是一个简化的示例,用作演示。关键是存在由#include行创建的依赖树森林。(此类树的根将是感兴趣的头文件,其子级是#include它的文件。其中一个树中的每个叶子表示一个文件,当感兴趣的头文件中发生更改时,必须编译该文件。树中的叶子数量随着深度呈指数增长,因此移除树枝(通过用前向声明替换#include)会对重建时间产生巨大影响。或者也许可以忽略不计的影响。这是理论,不是实践。


我应该注意,与这个问题一样,这个答案侧重于编译时间,而不是要考虑的其他因素。这不应该是关于前向声明的利弊的全面指南,只是对它们如何节省编译时间的解释。

相关内容

  • 没有找到相关文章

最新更新