文章目录
什么是自包含
头文件自包含是指:一个头文件能够独立编译,不需要使用者在使用它之前先包含其他头文件。也就是说,头文件自己包含了它所需要的所有依赖(也就是其他必需头文件)
为什么需要自包含
- 避免隐藏依赖:使用者无需知道头文件内部依赖了什么
- 包含顺序无关:无论先 #include 谁,都能正常编译
- 便于维护:头文件改动不会因为依赖顺序而出现错误
- 测试方便:可以直接单独编译头文件验证
没有自包含的示例
假设我们有 Point.h 和 Rectangle.h,其中 Rectangle 使用了 std::string 和 Point
cpp
// Rectangle.h ------ 依赖外部提供 <string> 和 Point.h
#pragma once
// 注意:这里没有 #include <string>,也没有 #include "Point.h"
class Rectangle {
public:
Rectangle(Point p, std::string name); // 用到了 Point 和 std::string
private:
Point topLeft;
std::string name;
};
使用者的代码必须小心翼翼地按顺序包含
cpp
// main.cpp
#include <string> // 必须先包含 string
#include "Point.h" // 必须先包含 Point
#include "Rectangle.h" // 最后才能包含 Rectangle
int main() {
Rectangle r(Point{0,0}, "r1");
return 0;
}
如果顺序错了,比如
cpp
#include "Rectangle.h" // 先包含,直接编译失败!
#include <string>
#include "Point.h"
编译错误:'Point' was not declared / 'std::string' has not been declared
自包含的头文件
cpp
// Rectangle.h ------ 自己包含所需的一切
#pragma once
#include <string> // 为了 std::string
#include "Point.h" // 为了 Point
class Rectangle {
public:
Rectangle(Point p, std::string name);
private:
Point topLeft;
std::string name;
};
使用时
cpp
// main.cpp
#include "Rectangle.h" // 一个就够,顺序任意
int main() {
Rectangle r(Point{0,0}, "r1");
return 0;
}
验证头文件是否自包含
最简单的办法:单独写一个只包含该头文件的 .cpp,尝试编译,如果这个文件能编译通过,说明头文件是自包含的;如果报错,就说明它有隐藏依赖
cpp
// test_header.cpp
#include "Rectangle.h"
// 什么都不写