编译链接模型
C++ 构建分两步:编译(每个 .cpp → 目标文件 .o)、链接(合并目标文件与库,解析外部符号成可执行)。声明与定义分离即为此服务。
g++ -c main.cpp -o main.o # 编译
g++ -c math.cpp -o math.o # 编译
g++ main.o math.o -o app # 链接分离编译让只改一个文件时只需重编它;链接阶段才把跨文件引用(函数/全局变量)绑定到一起。
头文件与守卫
头文件放声明(接口),.cpp 放定义(实现)。用 #pragma once 防止同一头文件被多次包含导致的重定义。
// math.h
#pragma once
int add(int a, int b); // 声明
// math.cpp
#include "math.h"
int add(int a, int b) { return a + b; } // 定义同一变量/函数(非 inline)在整个程序只能有一份定义(One Definition Rule)。实现放 .cpp 即避免多翻译单元重复定义。
Makefile 基础
Makefile 用规则描述"目标:依赖→命令",仅重制变更部分的文件,是自动化构建的传统方式。
app: main.o math.o
g++ main.o math.o -o app
main.o: main.cpp math.h
g++ -c main.cpp
math.o: math.cpp math.h
g++ -c math.cpp
clean:
rm -f *.o appCMake 入门
CMake 是跨平台构建生成器,输出 Makefile 或 Ninja/IDE 工程。核心是 CMakeLists.txt。
cmake_minimum_required(VERSION 3.16)
project(demo)
add_executable(app main.cpp math.cpp)cmake -S . -B build # 生成构建系统
cmake --build build # 编译Target 化写法
现代 CMake 以 target 为中心:用 target_include_directories/target_link_libraries 把属性绑定到目标,而非全局变量。
add_library(math math.cpp)
target_include_directories(math PUBLIC include)
add_executable(app main.cpp)
target_link_libraries(app PRIVATE math)PUBLIC 表示"我自己用、依赖我也用";PRIVATE 仅自己用;INTERFACE 仅传递。精确声明可避免泄漏依赖。
静态库
静态库(.a/.lib)在链接时把代码复制进可执行,运行期无外部依赖,体积大但部署简单。
# 用 CMake add_library(math STATIC ...)
ar rcs libmath.a math.o # 或手动归档动态库与链接
动态库(.so/.dll)在运行期加载,多程序共享、体积小、可热更新,但需保证运行环境能找到它。
# CMake: add_library(math SHARED ...)
g++ -shared -fPIC math.cpp -o libmath.so
# 运行时通过 LD_LIBRARY_PATH 或 rpath 找到动态库升级需保持 ABI 兼容,否则依赖它的程序需在接口变更后重新编译。静态库则无此顾虑。
常见陷阱与 FAQ
通常是"声明了但没链接对应实现/库",或 .cpp 没加入构建。检查是否把源文件加入 target、库顺序是否满足链接器需求。
被多个翻译单元包含会产生多份定义,违反 ODR,链接期报错。模板与 inline 例外(允许多份等价定义)。
追求零部署依赖、简单分发用静态;追求体积小、多程序共享、热更新用动态。库很大且不常变时动态库更划算。