头文件只放声明不写实现,避免multiple definition错误;函数体、变量定义等必须放在.cpp中;用#ifndef或#pragma once防重复包含;.cpp需显式包含所需头文件。

头文件里只放声明,别写实现
很多人一上来就在 .h 文件里写函数体,结果编译时报一堆 multiple definition 错误。这是因为每个包含该头文件的 .cpp 都会把函数体复制一份,链接时发现同名符号重复定义。
正确做法是:头文件只负责“说清楚接口”,比如类定义、函数声明、extern 变量声明;所有具体逻辑(函数体、全局变量定义、模板特化等)必须挪到对应的 .cpp 文件里。
- 用
#ifndef/#define/#endif或#pragma once防止重复包含 - 避免在头文件中
#include <vector></vector>等标准库头文件后直接写std::vector<int> data;</int>—— 这是定义,不是声明 - 如果非要在头文件里写函数,只能是
inline函数或模板(它们本就要求定义可见)
每个 .cpp 文件要显式包含它依赖的头文件
不能指望 A.cpp 包含了 foo.h,B.cpp 就能自动用上里面的东西。C++ 是分离编译模型:每个 .cpp 是独立编译单元,必须自己把需要的声明都拉进来。
常见错误是删掉某个 #include 后代码还能编译通过——那只是因为其他头文件“顺带”包含了它,属于隐藏依赖,换编译器或改头文件顺序就崩。
- 按谷歌规范排序:
"xxx.h"(本文件对应头)→ 系统头(<vector></vector>)→ 项目内其他头("math/point.h") - 每个
.cpp第一行优先包含与自己同名的.h(如triangle.cpp先#include "triangle.h"),能第一时间暴露头文件自身是否自完备 - 路径写法别用
../include/vec3.h,而用项目根目录为基准的相对路径,比如"core/vec3.h"
main.cpp 不是万能胶水,别往里塞所有逻辑
看到 main.cpp 越来越长、开始出现 if (mode == "triangle") { ... } 块时,就是信号:该拆了。main 的职责只是启动、协调、传参,不该承担业务计算。
例如三角形面积计算,应该有独立的 triangle.h 和 triangle.cpp,main 里只做三件事:读输入 → 调 Triangle::area() → 打印结果。
- main 里 new 对象可以,但别写
for (int i = 0; i - 避免在 main 里直接访问底层数据结构(比如裸指针、数组下标),而是通过类接口操作
- 如果 main 开始处理命令行参数解析、配置加载、日志初始化,说明该抽成
app_launcher.cpp或引入 CLI 库了
CMakeLists.txt 里每个源文件都要显式列出
光把文件放在目录里不会自动编译。CMake 不会扫描目录找 .cpp,你得告诉它哪些文件参与构建。
最简写法是:add_executable(myapp main.cpp triangle.cpp)。漏掉任何一个 .cpp,对应的功能就彻底消失,且往往不报错——只是链接时找不到符号。
- 不要用
file(GLOB ...)自动收集源文件,它会让 CMake 失去对文件增删的感知,改了文件名却不触发重新生成构建系统 - 头文件不用列进
add_executable,但要用target_include_directories(myapp PUBLIC include)告诉编译器去哪里找#include "xxx.h" - 如果用了
target_link_libraries(myapp PRIVATE mathlib),确保mathlib的头文件路径也通过PUBLIC或INTERFACE透出,否则下游无法包含
真正卡住人的从来不是语法,而是头文件和源文件之间那层“谁该信谁”的契约关系。一个没加 #pragma once 的头文件、一个漏掉的 #include、CMake 里少写一个 .cpp 名字——这些地方不报错,但程序会在某次重构后突然拒绝链接,或者行为诡异。多文件不是为了炫技,是让每个文件只回答一个问题:“你提供什么?你依赖什么?”
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











