函数声明与定义必须分离,定义只能在.cpp文件中出现一次(odr原则),声明可多次;头文件仅放声明并加保护宏,实现放.cpp文件,编译时需链接所有.cpp文件。

函数声明和定义必须分离,否则链接失败
直接在 main.cpp 里写函数实现,再在另一个文件里“重复定义”同名函数,编译器会报 multiple definition 错误。C++ 要求每个函数**定义只能出现一次**(ODR 原则),但**声明可以多次**。
正确做法是:把函数实现(定义)放在一个 .cpp 文件中,比如 utils.cpp;在对应头文件 utils.h 中只写声明;所有需要调用它的源文件都包含这个头文件。
-
utils.h:只放int add(int a, int b);这类声明,开头加#ifndef UTILS_H / #define UTILS_H / #endif防止重复包含 -
utils.cpp:实现函数,#include "utils.h",写int add(int a, int b) { return a + b; } -
main.cpp:写#include "utils.h",然后直接调用add(1, 2)
编译时必须同时参与链接,缺一不可
只编译 main.cpp 是不够的——即使头文件声明了函数,链接器仍找不到 add 的实际代码。你得把所有 .cpp 文件一起交给编译器。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 命令行下:用
g++ main.cpp utils.cpp -o program,不能漏掉utils.cpp - 使用 CMake:确保
add_executable列表里包含所有源文件,比如add_executable(myapp main.cpp utils.cpp) - IDE(如 VS、CLion):把
utils.cpp加入工程源文件列表,不是仅加到项目目录里就完事
跨目录引用要检查相对路径和包含方式
如果 utils.h 在 src/lib/ 下,而 main.cpp 在项目根目录,#include "utils.h" 会失败。编译器默认只在当前目录和系统路径找头文件。
- 推荐写法:
#include "lib/utils.h",并确保编译时加-Isrc(让编译器把src/当作头文件搜索起点) - 避免用绝对路径或
#include "../src/lib/utils.h",可移植性差,重构时极易断裂 - 如果用了
#include <utils.h></utils.h>,那它会被当作系统头文件处理(跳过本地搜索),基本必报错,除非你真把它装进了系统 include 目录
静态成员函数或内联函数有特殊规则
如果你在类里定义了一个 static 成员函数,又或者用了 inline,它们的定义可以(甚至应该)放在头文件里,因为编译器需要看到完整定义才能展开或生成符号。
- 静态成员函数:在头文件中声明+定义,
.cpp中无需再写实现;但注意它属于类作用域,调用需用MyClass::func() - 内联函数:定义写在头文件里,且必须加
inline关键字(C++17 起constexpr函数默认 inline,但显式写更安全) - 这两类函数不参与常规链接流程,所以不会因多文件包含而报
multiple definition
utils.cpp 没被编译进最终链接,这是最常被跳过的一步。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










