首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
C++ 为什么一个类总要配两个文件?.h 和 .cpp 到底是什么关系?
📅 2026/10/15 11:40:29
✍️ 爱科研究院
👁 阅读 3,247
新手重点.h声明告诉编译器有什么东西不写具体干活代码.cpp实现真正写函数、类内部怎么干活的代码习惯上同名xxx.h↔xxx.cpp这不是语法强制是工程约定。注意C 语言是.h.cC/Qt 是.h.cpp。一、为什么要分成两个文件1头文件.h放声明只对外公布有哪些类、哪些函数、变量长什么样。只说 “有这个东西”不写函数内部逻辑。 编译器看到.h就知道哦存在这个类 / 函数可以调用。2源文件.cpp放实现定义写函数大括号{}里面真正执行的代码完成具体逻辑。如果全部写在一个 .h 文件会发生什么假设把函数实现全部写在 h 里多个 cpp 同时#include xxx.h#include本质就是把 h 的文本直接复制粘贴到 cpp 里面。 多个文件复制同一份函数实现 →重复定义报错multiple definition。✨核心原理#include a.h不是导入模块是文本拷贝。举个通俗比喻.h产品说明书。告诉你这个设备有什么功能、接口长啥样。没有内部电路板。.cpp设备内部电路板真正干活。别人不需要看电路板只看说明书即可。多人开发的时候别人只需要拿你的.h说明书就可以调用你的类不需要看你复杂的实现代码。二、为什么习惯同名test.h和test.cpp⚠️语法不强制同名可以 a.h 配 b.cpp编译照样跑。但是工程上 100% 全部同名原因方便人看代码看到test.h立刻知道实现代码在test.cpp不用到处找。一一对应一个类一套 h‑cpp代码模块清晰。Qt 自动生成的代码projectdialog.h自动生成projectdialog.cppQt 工具链强制这套命名。反面例子语法合法但是没人这么写phone.h类 MobilePhone 声明implement_all.cpp全部类实现堆这里项目大了完全无法维护。三、两者之间有什么联系test.cpp第一行必须写#include test.h作用把头文件的类、函数声明拷贝进来这样 cpp 里面写类外实现的时候编译器认识这个类。完整最小示例test.h头文件声明#ifndef TEST_H #define TEST_H // 只声明类告诉编译器存在类 Person成员是什么不写函数内部代码 class Person { public: void sayHello(); // 函数声明有一个sayHello函数没有{}内部代码 int age; }; #endif // TEST_H逐词解释#ifndef TEST_H头文件保护宏。防止同一个 h 被多次 include重复拷贝造成重复定义。#define TEST_H标记这个头已经被引入过。#endif结束保护。class Person{...}类的声明void sayHello();只有分号没有函数体只是声明。test.cpp源文件实现#include test.h // 【关键联系】把test.h拷贝进来认识Person类 // 类外实现语法 类名::函数名() void Person::sayHello() { // 这里才写真正干活的代码 }拆解#include test.h→ 将 test.h 全部文本复制到此处。Person::sayHello()Person类名::作用域解析符号代表这个sayHello属于 Person 这个类。如果 cpp 不写#include test.h会怎么样 编译器看不懂Person是什么直接报Person: undeclared identifier未识别标识符。main.cpp主程序使用这个类#include test.h // main只需要包含头文件就可以使用Person类 int main() { Person p; p.sayHello(); return 0; }main.cpp不需要 include test.cpp千万不要写#include test.cpp编译层面发生了什么非常关键编译器分别编译每一个.cpp文件生成独立的.obj目标文件test.cpp → test.obj里面有 sayHello 的机器码main.cpp → main.obj里面 main 函数调用 sayHellomain.cpp 只包含 test.h只有声明没有 sayHello 实现代码。此时编译器只知道有这个函数先放行。链接器 linker把所有 obj 拼在一起。main.obj 里面需要的sayHello函数去 test.obj 里面找到把两者拼接成 exe 程序。分工记忆编译阶段每个 cpp 单独干活靠.h做声明编译器只需要知道 “函数长啥样”。链接阶段把各个 obj 文件合并找真正的函数实现代码。简易流程图文字版替代图文test.h (声明类Personvoid sayHello(); ) ↑ │ #include test.h test.cpp main.cpp ↓ ↓ 编译 编译 test.obj main.obj ↘ ↙ 链接器 linker ↓ exe可执行程序四、哪些东西放 h哪些放 cpp✅.h 头文件放声明类的定义class XX{ };函数原型只有分号没有函数体void func();常量、宏、#include、命名空间Qt 的Q_OBJECT信号槽声明✅.cpp 源文件放实现 / 定义#include xxx.h类外实现函数XX::func(){ ... }大括号内部业务代码全局变量的定义❌ 不要在 h 里面写大段函数实现除非模板、inline 内联函数五、结合你前面 Qt 项目看真实案例你之前代码test_widget_projectdialog.h类的声明构造、析构函数声明ui 指针test_widget_projectdialog.cpp第一行#include test_widget_projectdialog.h然后写test_widget_projectdialog::test_widget_projectdialog(QWidget *parent) :QWidget(parent), ui(new Ui::xxx) { ui-setupUi(this); }.h告诉编译器这个类有构造、析构。.cpp写构造函数内部到底做什么。 Qt 的 UI 指针Ui::xxx *ui;写在 h实例化 new 写在 cpp。六、新手高频踩坑❌#include xxx.cpp绝对禁止include 源文件会出现重复定义。只 include.h。❌ 头文件忘记写#ifndef /#define /#endif多重包含报一堆错误。❌ cpp 忘记#include xxx.h类名识别失败。❌ h 写了函数声明cpp 忘记写实现。编译能过链接报错undefined reference未找到引用。
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/15 11:40:29
AI Coding的演变历程
2026/10/15 11:40:29
如何把SoulX-FlashHead接入自有应用:Python集成与批量数字人视频生成开发指南
2026/10/15 11:40:29
软考「系统规划与管理师(第2版)」冲刺|第一篇案例解析与关键要点
2026/10/15 13:30:59
OpenClaw Sandbox工作原理解析(一):Docker容器隔离与沙箱配置入门
2026/10/15 13:30:59
SAS PROC FASTCLUS实现K-均值聚类:从数据清洗到客户分群落地
2026/10/15 13:30:59
计算机新手入门指南:从基础到进阶学习路径
2026/10/15 13:30:59
基于Python Flask的社区物业报修系统开发实践
2026/10/15 13:30:58
小程序前端用 Canvas 直接生成二维码与海报的完整实践与避坑指南
2026/10/15 13:25:58
手写K-means聚类算法:天气数据分类实战与踩坑记录
2026/10/15 0:02:59
AI Coding时代的柔性之美:从静态软件到自演化软件,软件研发正在重新进化
2026/10/15 0:02:59
亨得利客户服务中心官方网点分布一览:2026年全国多城门店地址与最新联系电话公布
2026/10/15 0:02:59
防关联浏览器哪个好?三款产品实测差异解析
2026/10/13 12:25:09
流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南
2026/10/14 18:35:19
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别
2026/10/14 14:52:59
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容
2026/10/15 1:07:55
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/15 5:07:59
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/15 8:10:01
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)