c++ - 预编译 header 的实际工作原理

标签 c++ visual-c++ precompiled-headers

我所有的问题都与 vc++ 编译器有关,但我猜其他 c++ 编译器也有相同的行为。

  1. 预编译头文件是与预处理器相关的东西还是与编译过程有关?或两者?我有几个猜测:
    • PCH-engine 仅扩展宏定义和嵌套 header 并将它们转换为二进制格式(pch 文件)。在这种情况下,所有源文件(我的意思是 cpp/hpp 也可能包含在 PCH 中)将在项目中的每个源文件中重新编译。还是不行?
    • 所有源文件将只编译一次并拉入单个 obj 文件?比如这个例子中会编译多少次变体库? IE。只有一次 - 在 PCH 中或两次 - 不在 PCH 中但在两个 *.cpp 文件中或三次 - 在 PCH 和两个 *.cpp 文件中?为什么?

//stdafx.h
#include <boost/variant/variant.hpp>

//test1.cpp
#include "stdafx.h"
#include <boost/variant/variant.hpp>
...

//test2.cpp
#include "stdafx.h"
...
  1. 我应该将哪些文件放入预编译头文件中?我想这是在项目中到处使用并且很少改变的东西。图书馆呢,例如 boost?我们只在少数源文件中使用 boost,我们应该把它放在 PCH 中吗?

最佳答案

我对 VC++ 的内部结构没有特别的了解。然而,如果对编译器设计和理论有所了解,那么这些所谓的“预编译头文件”只不过是经典编译器设计的初始词法分析和标记化阶段的结果。

考虑一个包含以下内容的简单头文件:

#ifdef FOO
#define BAR 10
#else
#undef FOOBAR
class Foo {
public:
     void bar();
};
#include "foobar.h"
#endif

您必须明白,使用所谓的“预编译”头文件的效果必须与使用头文件原样相同。

在这里,你真的不知道这个头文件要做什么。这完全取决于实际包含头文件时定义的预处理器宏。你不知道这个头文件会定义哪些宏。您不知道这个头文件将取消定义哪些宏。你不知道这个头文件还会包含哪些其他头文件。在这里,您真的了解不多。

从概念上讲,要“预编译”头文件,您唯一可以做的就是预解析它。将语言的各个元素、各个关键字(如“#ifdef”、“class”和所有其他关键字)转换为各个二进制标记。删除任何注释、空格等...

编译传统语言的第一阶段涉及将纯文本源解析为内部语言元素。词法分析和标记化阶段。在解析了各个语言元素之后,将尝试弄清楚生成的、解析后的源代码应该如何转化为目标模块。这就是编译器 99% 的工作所在。最初的词法分析阶段并不多,但这几乎是您“预编译”源代码并保存标记化源的内部二进制表示的全部内容,因此可以跳过此阶段,当实际代码使用“预编译”源代码进行编译。

我假设 VC++ 对预编译头文件的内容几乎没有限制,甚至根本没有限制。但是,如果有一些限制——比如,预编译头文件不能有任何条件预处理器指令(ifdef/ifndef),除了经典的守卫——那么就可以做更多的工作来生成预编译头文件,并保存一个在这里做更多的工作。对预编译 header 内容的其他限制也可能导致一些额外的功能被转移到预编译阶段。

关于c++ - 预编译 header 的实际工作原理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36499328/

相关文章:

uwp - 如何使用多个预编译头文件;一些来自图书馆

c++ - 使用自动工具检查预编译头文件?

c++ - 不应该有虚函数隐藏的警告吗?

visual-c++ - GetModuleHandle 函数是否会导致 VC++ 中的任何泄漏(堆栈溢出和内存泄漏)

c++ - 使用 boost 库时加快编译/链接时间

visual-c++ - IAccessible 接口(interface)的覆盖函数在 cwnd 驱动的类中不起作用

c++ - GetProcessImageFileName 和 LPTSTR

c++ - 我正在尝试获取数字的随机顺序 1 :16 using srand and rand. 代码按预期工作,但仅进行到第 12 次迭代。为什么?

c++ - quickfix/c++ 中的重复组

c++ - clang-tidy:什么可能导致 NOLINT 评论不被尊重?