include头文件是编译瓶颈,因重复解析、i/o与语义分析叠加开销;预编译头(pch)通过提前编译稳定头文件为二进制态来加速,但需内容稳定、顺序固定、首行包含,且不能含易变项目头文件。

为什么 #include 头文件是编译瓶颈
大型 C++ 项目里,90% 的编译时间花在重复解析标准库、Boost、Qt 等头文件上。每次 #include <vector></vector>,编译器都得重读、重解析、重模板实例化一遍 —— 即使这个文件在十个源文件里都被包含。这不是 CPU 不够快,是 I/O + 预处理 + 语义分析的叠加开销。
- 头文件越多、嵌套越深(比如
QMainWindow拉进来 50+ 层),单个.cpp编译越慢 -
#include "xxx.h"直接暴露实现细节,一改头文件,所有依赖它的.cpp全得重编 - 预编译头(PCH)本质是把“稳定、庞大、到处用”的头文件提前编译成二进制中间态,跳过重复解析
怎么写一个真正生效的 stdafx.h 或 precompiled.h
名字不重要,关键是内容稳定、顺序固定、且被所有源文件一致包含。VC++ 默认叫 stdafx.h,Clang/GCC 常用 precompiled.h,但规则一样:它必须是每个 .cpp 的第一行非空非注释代码。
- 只放**极少变动**的系统/第三方头:如
<vector></vector>、<memory></memory>、<qstring></qstring>(Qt 项目)、<boost></boost> - 绝对不要放项目自己的
"*.h"—— 它们变频繁,一改就让整个 PCH 失效,反而拖慢编译 - 避免宏定义污染:像
#define UNICODE这类影响后续头行为的宏,必须放在 PCH 里,且不能在 PCH 之后再定义 - VC++ 要求
/Yu"stdafx.h"和/Yc"stdafx.h"配对;Clang/GCC 用-include precompiled.h -x c++-header生成,再用-include precompiled.h使用
#pragma once 和 include guards 对 PCH 有影响吗
没有。PCH 生效的前提是预处理器已经完成了头文件展开,而 #pragma once 和 #ifndef XXX_H 都只是防止重复包含,发生在预处理阶段早期。它们不影响 PCH 是否被加载,但会影响 PCH 内部是否被多次展开 —— 所以 PCH 文件自身必须带防重机制。
-
#pragma once更轻量,但某些老旧编译器(如旧版 ICC)不支持;include guards兼容性好,但命名冲突风险略高(比如两个库都用COMMON_H) - 如果你在 PCH 里写了
#include <string></string>,而某个.cpp又在 PCH 之后手动#include <string></string>,预处理器会直接跳过——这没问题,PCH 已提供符号 - 真正危险的是:PCH 里没包含
<algorithm></algorithm>,但你在.cpp里用了std::sort又忘了#include <algorithm></algorithm>—— 这时可能靠 PCH 里的间接包含“侥幸”通过,但行为不可靠,换编译器或改 PCH 就崩
Clang/GCC 下启用 PCH 的实际命令和常见报错
Clang 和 GCC 的 PCH 支持比 VC++ 更“裸”,没 IDE 自动管理,出错时提示也更晦涩。关键不是生成,而是确保使用时的编译参数、头路径、语言标准完全一致。
立即学习“C++免费学习笔记(深入)”;
- 生成 PCH:
clang++ -x c++-header -std=c++17 -I./include precompiled.h -o precompiled.h.pch - 使用 PCH:
clang++ -include precompiled.h -std=c++17 -I./include main.cpp(注意:-include 必须在源文件之前) - 报错
precompiled.h.pch: not compatible with this compiler:说明生成和使用时的-std=、-D、-I不一致,哪怕多一个-DDEBUG都会导致不兼容 - 报错
precompiled.h: No such file or directory:不是找不到.pch,而是找不到precompiled.h——-include是按头文件名找的,不是 .pch 名










