C++ 包含保护(Include Guard)完全指南

Search for a command to run...

No comments yet. Be the first to comment.
一、有序对和笛卡尔积 1、有序对(序偶):由两个元素 x 和 y 按照确定顺序排列组成的二元组,记作⟨x, y⟩ 2、笛卡尔积:以 A 中元素为第一元、B 中元素为第二元,构造所有有序对⟨x,y⟩; 由全部这类有序对构成的集合,称为 A 与 B 的笛卡尔积,记作 AXB 例题: 已知 A={a,b}, B={0,1,2},求笛卡尔积 A × B、B × A A × B(前元取自 A,后元取自 B

1、集合的概念 N元子集:含有n个元素的子集叫做N元子集. 例题:已知集合 A={1,2,3},按元素个数对 A 的所有子集分类 0元子集:∅ 1元子集:{1}, {2}, {3} 2 元子集:{1,2}, {1,3}, {2,3} 3 元子集:{1,2,3} 幂集:设 A 为集合,由 A 的全部子集构成的集合称为 A 的幂集,记作P(A). 例题:A={1,2,3},求A的幂集 答案:P

一、基本元件 元件 符号 说明 例子 个体词 a, b, c... 代表具体对象(常元) a:小明 个体变元 x, y, z... 代表任意对象 x 表示论域中任一元素 谓词 P(x), Q(x,y)... 表示性质或关系 P(x):x是学生; L(x,y):x喜欢y 量词 ∀, ∃ 修饰个体范围 ∀x(所有x); ∃x(存在x) 连接词 ¬, ∧, ∨, →, ↔ 命

本质是"逻辑推导游戏",给定几个前提,用固定规则一步步推出结论。套路固定,背下规则就能拿分。 一、核心推理规则公式: 假言推理(MP)A→B, A ⇒ B肯定前件→肯定后件拒取式(MT)A→B, ¬B ⇒ ¬A否定后件→否定前件假言三段论(HS)A→B, B→C ⇒ A→C蕴含传递析取三段论(DS)A∨B, ¬A ⇒ B否定一边得另一边附加律A ⇒ A∨B或上一个随便什么化简律A∧B ⇒ A (或

定义:S是一个联结词集合,若任一个命题公式都可以由s中的联结词表示出来命题公式与之等价,则称S是一个联结词完备集。 也就是说一个连接词集合,能表达出所有真值函数,称为完备集 以下是完备集: S1={¬,∧,∨} —— 否定、合取、析取 S2={¬,∧,∨,→} —— 否定、合取、析取、蕴涵 S3={¬,∧,∨,→,↔} —— 否定、合取、析取、蕴涵、等价 S4={¬,∧} —— 否定、

包含保护是 C++ 中解决头文件重复包含导致重复定义错误的核心技术,核心目标是让头文件内容在一次编译过程中仅被处理一次。
C++ 的#include指令会将头文件内容直接复制到包含它的源文件中。若一个头文件被间接 / 直接多次包含(如:A.h 包含 B.h,C.cpp 同时包含 A.h 和 B.h),会导致 B.h 内容重复编译,触发redefinition of class /function错误。
先判断宏是否未定义 → 再定义宏 → 最后编译头文件内容,是 C++ 标准支持的通用方案。
// person.hpp - 经典宏定义包含保护
// 第一步:判断PERSON_HPP_INCLUDED宏是否未被定义(if not defined)
#ifndef PERSON_HPP_INCLUDED
// 第二步:若未定义,立即定义该宏(标记头文件已被包含)
#define PERSON_HPP_INCLUDED
// 第三步:仅宏未定义时,编译以下内容
#include <string>
class Person {
private:
std::string name;
int age;
public:
Person(std::string n, int a) : name(n), age(a) {}
void showInfo();
};
// 结束#ifndef条件判断块
#endif // PERSON_HPP_INCLUDED
宏名必须唯一,建议格式:文件名大写 + 下划线 + INCLUDED(如UTILS_HPP_INCLUDED、STUDENT_MANAGE_HPP_INCLUDED);
禁止使用标准库名称(如STRING_HPP)或单下划线开头的命名(C++ 保留); 备注:宏名重复会导致包含保护失效,引发重复定义错误。
#pragma once方式(简洁高效)直接告知编译器「该文件仅编译一次」,非 C++ 标准但主流编译器(GCC/Clang/MSVC)均支持。
// person.hpp - #pragma once方式
#pragma once // 核心指令,放在头文件第一行
#include <string>
class Person {
private:
std::string name;
int age;
public:
Person(std::string n, int a) : name(n), age(a) {}
void showInfo();
};
优点:代码简洁,无宏名冲突风险;
缺点:非 C++ 标准(极少数老旧编译器不支持); 备注:日常开发优先使用该方式,跨平台场景建议用宏定义方式。
_Pragma方式(C++11 标准,极少使用)C++11 标准化的#pragma替代方案,语法复杂,实际开发中几乎不用。
// person.hpp - _Pragma方式
_Pragma("once")
#include <string>
class Person {
private:
std::string name;
int age;
public:
Person(std::string n, int a) : name(n), age(a) {}
void showInfo();
};
| 错误写法(逻辑失效) | 正确写法(核心逻辑) | 后果 / 效果 |
|---|---|---|
#define XXX(先定义) |
||
#ifndef XXX(后判断) |
#ifndef XXX(先判断) |
|
#define XXX(后定义) |
头文件内容永远不编译 | |
| 头文件内容仅编译一次 |
| 指令 / 宏结构 | 参数 | 功能 | 备注 |
|---|---|---|---|
#ifndef |
自定义宏名(如 PERSON_HPP_INCLUDED) | 判断指定宏是否未被定义,若为真则执行后续代码块 | 必须与#endif配对使用 |
#define |
自定义宏名(如 PERSON_HPP_INCLUDED) | 定义指定宏,无返回值,仅标记「宏已存在」 | 需在#ifndef为真时执行,否则失效 |
#endif |
无参数 | 结束#ifndef,#ifdef等条件编译块 |
必须与#ifndef一一对应,缺一不可 |
#pragma once |
无参数 | 告知编译器当前文件仅编译一次 | 非 C++ 标准,主流编译器兼容 |
_Pragma |
字符串形式的指令 | C++11 标准的编译指令接口,替代#pragma |
语法复杂,极少实际使用 |
核心目标:解决头文件重复包含导致的重复定义错误;
主流方案:#pragma once(简洁)、宏定义(#ifndef/#define/#endif,全兼容);
关键规则:宏定义方式需保证宏名唯一,禁止混合使用多种包含保护方式。