前向声明与包含


18

Reduce the number of #include files in header files. It will reduce build times. Instead, put include files in source code files and use forward declarations in header files.

我在这里阅读。http://www.yolinux.com/TUTORIALS/LinuxTutorialC++CodingStyle.html

因此它说如果头文件中的类(类A)不需要使用某个类(类B)的实际定义。那时我们可以使用前向声明,而不是包括特定的(B类)头文件。

问题:如果标头中的类(类A)没有使用特定类(类B)的实际定义,那么正向声明如何帮助减少编译时间?

Answers:


12

编译器不在乎类A是否使用类B。它仅知道编译类A并且没有类B的在先声明(正向声明或其他方式)时,它会感到慌乱并将其标记为错误。

在这里重要的是,编译器知道在您的猫踩在键盘上并创建了一些可能会或可能不会解释为类的随机字母后,您并未尝试编译程序。

当它看到包含文件时,为了能够使用其中包含的任何信息,它必须打开文件并对其进行解析(无论它是否确实需要这样做)。如果该文件中包含其他文件,则也必须打开并分析这些文件,等等。如果可以避免,通常最好使用前向声明。

编辑此规则的例外是预编译头。在这种情况下,所有标头都将被编译并保存以供将来编译。如果头文件没有更改,则编译器可以巧妙地使用以前编译中的预编译头文件,从而减少了编译时间,但只有当您不需要经常更改头文件时,它才能很好地工作。


谢谢你的解释。然后确定为例,你认为有三个头文件vehicle.hbus.htoybus.hvehicle.hinclude by bus.hbus.hinclude by toybus.h。所以如果我做一些改变bus.h。编译器是否打开并vehicle.h再次解析 ?它会再次编译吗?
Nayana Adassuriya

1
@NayanaAdassuriya是的,它每次都会被包含和解析,这也是为什么您在头文件中看到#pragma once#ifndef __VEHICLE_H_键入声明以防止多次包含此类文件(或至少在ifndef情况下使用多次)的原因。
尼尔

4

因为这样A.hpp不需要#include B.hpp

所以A.hpp变成

class B;//or however forward decl works for classes

class A
{
    B* bInstance_;
//...
}

因此,当包含A.hpp时,则不会隐式包含B.hpp,并且每次b.hpp更改时都不需要重新编译仅依赖于A.hpp的所有文件。


但在源文件(A.cpp)中。需要包括实际的头文件(Bh)。因此,每次需要编译时。最后,双向Bh都需要重新编译更改。有什么不同吗?
Nayana Adassuriya

@NayanaAdassuriya否,因为A仅使用指向B的指针,并且对B的更改不会影响A.hpp(或包含它的文件)
棘手怪胎

@NayanaAdassuriya:是的,A.cpp将不得不重新编译(如果它在A的方法体内使用B的定义,但通常这样做),但是C.cpp(它直接使用A,而不直接使用B)将不需要。
Jan Hudec

3

请记住,C / C ++预处理程序是一个单独的纯文本处理步骤。该#include指令提取包含的标头的内容,编译器必须对其进行解析。而且,每个编译.cpp都是完全独立的,因此编译器在编译时仅对其进行解析的事实在编译B.hB.cpp再次需要它时至少没有帮助A.cpp。并再次编译时C.cpp。和D.cpp。等等。如果其中的任何文件已更改,则必须重新编译每个文件。

所以说类A应用类B和类CD利用类A,但不需要操作B。如果类A可以用的只是向前声明中声明B,不是B.h被编译两次:编译时B.cppA.cpp(因为B仍然需要内部A的方法)。

但是,当A.hinclude时B.h,它会被编译四次 -编译B.cppA.cppC.cpp以及D.cpp之后的两个现在也间接包含在内B.h

同样,当头文件被多次包含时,预处理器仍然必须每次都读取它。由于保护#ifdefs,它将跳过对其内容的处理,但是它仍然会读取它,并且需要搜索保护的末尾,这意味着它必须解析内部的所有预处理器指令。

(如另一个答案中所述,预编译的头文件试图解决此问题,但是它们是蠕虫的罐头;基本上,您可以合理地将它们用于系统头文件,并且前提是您不使用过多的头文件,但对于项目中的标题)


+1,标头包含仅在您拥有大量类时才成为一个严重的问题,而在只有两个A类和B类时则不然。所有其他帖子似乎都缺少这个中心点。
Doc Brown

2

与整个头文件本身可能包含更多头文件相比,前向声明的解析速度要快得多。

同样,如果您在类B的头文件中更改了某些内容,则包括该头的所有内容都必须重新编译。使用前向声明,它可能仅是A实现所在的源文件。但是,如果A的标头实际上包含B的标头,则包括A的所有内容a.hpp也将重新编译,即使它不使用B的任何内容。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.