准备开一个新的系列, cracking c++。是看 https://hackingcpp.com/ 这个网站的相关学习记录。
本文是第一篇,是学习C++中 #include
的用法。这里假定读者知道可以用 -I
参数来告诉 g++/clang++ 搜索路径。
C++ 的 hello world 程序如下:
#include
// our first program
int main ()
{
std::cout << "Hello World\n";
}
本文只考虑第一行代码 #include
的理解。
#include
是预处理指令,
通常理解为一个名为 iostream
的文件, 并且 iostream
文件能被编译器找到。
#include
和 #include "xx"
的区别?这两种预处理指令用法, 都是编译器实现决定的, 换言之C和C++标准并没有给出明确的预期行为。
而实际使用的C/C++编译器,可以认为都把这两个指令实现为 “包含文件”, 虽然理论上说编译器可以自行定义 “header”, header 不必是文件的形式, 然后包含这个 header。
而对于这两者的区别, 那更是编译器实现所决定的: 可以去翻看各个C/C++编译器的文档,不过其实没啥必要 – 观察现有的流行 C++ 库, 看看他们怎么用的?
更具体说, 对于一个成熟的C++开源项目, 它的实现通常不止一个文件,也不止一层目录结构, 那么它的核心实现中, 包含自己定义的源代码时, 是用 #include
还是 #include "xx/yy.hpp"
呢?
#include "xx/yy.h"
流leveldb 是知名数据库,谷歌开源,Jeaf Dean 代表作
https://github.com/google/leveldb/blob/main/db/db_impl.cc
#include "db/builder.h"
#include "db/db_iter.h"
#include "db/dbformat.h"
#include "db/filename.h"
#include "db/log_reader.h"
#include "db/log_writer.h"
#include "xx/yy.h"
流rocksdb 在代码风格上继承了 leveldb
https://github.com/facebook/rocksdb/blob/main/db/column_family.cc
#include "db/blob/blob_file_cache.h"
#include "db/blob/blob_source.h"
#include "db/compaction/compaction_picker.h"
#include "db/compaction/compaction_picker_fifo.h"
#include
流pytorch 是知名的深度学习框架
https://github.com/pytorch/pytorch/blob/main/torch/csrc/tensor/python_tensor.cpp
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "xx/yy.h"
流googletest 是知名的单元测试框架
https://github.com/google/googletest/blob/main/googletest/src/gtest-death-test.cc
#include "gtest/internal/custom/gtest.h"
#include "gtest/internal/gtest-port.h"
#include "gtest/gtest-message.h"
#include "gtest/internal/gtest-string.h"
#include "src/gtest-internal-inl.h"
#include "xx/yy.hpp"
流opencv 是知名的 计算机视觉库
https://github.com/opencv/opencv/blob/4.x/modules/core/src/buffer_area.cpp
#include "opencv2/core/utils/buffer_area.private.hpp"
#include "opencv2/core/utils/configuration.private.hpp"
#include
和 #include "xx/yy.h"
混用流https://github.com/grpc/grpc/blob/master/src/core/lib/gpr/alloc.cc
#include
#include
#include
#include
#include
#include "src/core/lib/gprpp/crash.h"
#include "xx/yy.h"
流mlir 是 llvm 中的一个模块,可用于深度学习编译器开发
https://github.com/llvm/llvm-project/blob/main/mlir/lib/Dialect/AMDGPU/IR/AMDGPUDialect.cpp
#include "mlir/Dialect/AMDGPU/IR/AMDGPUDialect.h"
#include "mlir/Dialect/Arith/IR/Arith.h"
#include "mlir/Dialect/GPU/IR/GPUDialect.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinTypes.h"
#include "mlir/IR/Diagnostics.h"
#include "mlir/IR/DialectImplementation.h"
#include "mlir/IR/Matchers.h"
#include "mlir/IR/OpImplementation.h"
#include "mlir/IR/PatternMatch.h"
#include "mlir/IR/TypeUtilities.h"
#include "llvm/ADT/TypeSwitch.h"
可以看到对于一个文件 xx.h
可以有如下5种包含方式, 都是可以的, C++纯度越高的项目,使用的方式越靠“下方”,不过其实差别不大; 可以直接无脑#include
.
#include "xx.h"
// bad, no sub directory as prefix#include "xx/yy.h"
// ok. header seems provide C API#include
// ditto#include "xx/yy.hpp"
// ok. header provides C++ API#include
// dittohttps://stackoverflow.com/questions/21593/what-is-the-difference-between-include-filename-and-include-filename?answertab=trending#tab-top