应优先使用 #ifndef / #define / #endif,因其是标准、可移植、可靠;#pragma once 虽简洁但非标准,存在文件系统依赖和兼容性风险。
用 #ifndef 或 #pragma once 都能防止头文件重复包含,但它们机制不同、兼容性不同、行为边界也不同——选错可能在跨平台或大型项目中埋坑。
多次包含同一头文件,会导致符号重定义(比如类重复声明、函数重复声明)、模板实例化冲突、编译变慢。C++ 标准不保证头文件被多次包含时的行为安全,必须主动防护。
常见触发场景:
#include "common.h",而它们又被同一个 .cpp 同时包含A.h → B.h → C.h,同时 A.h → C.
h)它依赖宏名唯一性,由预处理器在文本层面判断是否跳过内容。只要宏名不冲突,就可靠。
典型写法(注意命名规范):
#ifndef MYLIB_VECTOR_H_ #define MYLIB_VECTOR_H_include
namespace mylib { template
class vector { / ... / }; } // namespace mylib endif // MYLIB_VECTORH
关键点:
大写 + 下划线 + 文件路径信息(如 UTILS_LOG_H_),避免和用户代码/第三方库冲突#ifndef 和 #endif 之间不能有未配对的条件编译指令#include <...> 和 #include "..." 同时引用,也能正确识别为同一文件(靠路径字符串匹配)它让编译器直接按物理文件路径做去重,不依赖宏名,写起来更轻量:
#pragma onceinclude
namespace mylib { template
class vector { / ... / }; }
但它的问题藏在细节里:
#pragma once 可能认为是两个文件/src/a.h 和 /build/src/a.h 是硬链接),而 #ifndef 因宏名相同仍能生效没有绝对优劣,只有上下文适配:
#ifndef,确保可预测性和最大兼容性#pragma once 提高可读性,但需在 CI 中验证所有目标平台#pragma once + 保留 #ifndef 注释作为兜底(非强制,仅提醒),但不要真写两套防护真正容易被忽略的是:头文件防护只是第一道防线;如果头文件本身有定义非内联函数、全局变量或模板以外的实体,还需配合 inline、static、extern 或定义分离(声明放头文件,定义放 .cpp)来避免 ODR 违规——防护宏管不了链接期错误。