前置知识
- Linux/macOS 终端基础(
cd、ls、grep、管道) - 至少熟悉一种编程语言(Java/Python/Go 均可)
- 知道
gcc / g++ 是什么
为什么选 vim 写 C++
C++ 是少有的"几十年没退出主流"的语言——从 1985 年 Bjarne Stroustrup 发布第一个商用版本,到 2011 年的 C++11、2014 年的 C++14、2017 年的 C++17,模板元、RAII、移动语义、concept(20 概念)层层叠加,IDE 也跟着膨胀:Visual Studio 安装动辄 10GB+,CLion 启动慢、内存吃紧,Eclipse CDT 配置繁琐。
在很多 Linux 老兵眼里,vim + 一组插件就是写 C++ 的"瑞士军刀"——启动快、键盘流不离开主键区、ctags / cscope 跳转精准、配合 gdb 调试毫不输图形 IDE。2010 年代之前,Linux 内核、Hadoop、Chrome 等大型 C++ 项目的核心开发者都靠 vim 写代码。
但这条路有三个关键节点必须打通:语法高亮 → 编译环境 → 调试工具链 → 自动化构建。任何一个断了,体验就掉一档。
一、vim 基础:C++ 语法与缩进
1.1 系统自带的语法文件
1
2
| # 多数 Linux 发行版已经自带
ls /usr/share/vim/vim*/syntax/cpp.vim
|
打开 ~/.vimrc,加一行让 vim 识别 .h .hpp .cc .cpp .cxx:
1
2
3
4
| " C/C++ 文件类型识别
au BufRead,BufNewFile *.h,*.hpp,*.cc,*.cpp,*.cxx set filetype=cpp
syntax on
filetype indent on
|
效果:class template namespace auto 关键字高亮,注释块折叠,多行字符串着色。
1.2 缩进策略
C++ 缩进的核心矛盾是 public: / private: 该不该比 class 缩进一格、K&R 还是 Allman 花括号风格。vim 自带的 cpp 缩进文件已经能 cover 80%:
1
2
3
4
5
6
7
8
| " 缩进宽度 4 空格、tab 转空格
set tabstop=4
set shiftwidth=4
set expandtab
set autoindent
" C/C++ 缩进风格
set cindent
set cinoptions=:0,g0,t0,(0
|
:0 让 public: 不缩进、g0 让 C 风格作用域内成员缩进、t0 让函数返回值类型不强制换行——配合 :set cinoptions+=L1 还可以让 case 不缩进。
二、编译工具链:gcc / g++ / clang
2.1 gcc 与 g++ 的区别
gcc 是 GNU C Compiler,g++ 是 GNU C++ Compiler——后者的本质是 gcc -xc++ -lstdc++ -shared-libgcc。C++ 项目一律用 g++,混用 gcc 会在链接 stdc++ 时报一堆 undefined reference。
1
2
3
| # 验证版本(C++17 已经是 2017 标准,g++ 7+ 完整支持)
g++ --version
# g++ (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0
|
2.2 经典编译命令
1
2
3
4
5
6
7
8
9
10
| # 单文件
g++ -std=c++17 -Wall -Wextra -O2 -o hello hello.cpp
# 多文件
g++ -std=c++17 -Wall -O2 main.cpp foo.cpp bar.cpp -o myapp
# 分两步:先 -c 生成 .o,再链接
g++ -std=c++17 -Wall -O2 -c main.cpp -o main.o
g++ -std=c++17 -Wall -O2 -c foo.cpp -o foo.o
g++ main.o foo.o -o myapp
|
-Wall -Wextra 开启几乎所有警告——零警告是 C++ 老兵的第一铁律。
2.3 clang 替代
macOS 默认编译器就是 clang++,Linux 上也能装——编译错误信息比 gcc 友好、静态分析更强:
1
2
3
4
| # Ubuntu 安装
sudo apt install clang
clang++ --version
# clang version 10.0.0
|
很多大项目(Chrome、LLVM 本身)默认用 clang++,编译器版本切换可以用 update-alternatives:
1
2
| sudo update-alternatives --config c++
# 出现菜单:g++ / clang++ 选其一
|
三、调试:gdb 基础
3.1 编译时开调试
1
2
3
| g++ -std=c++17 -Wall -g -O0 -o debug_demo demo.cpp
# -g 生成调试信息(GNU debug 格式:DWARF)
# -O0 关闭优化,gdb 单步能看到每行
|
生产构建用 -O2、调试构建用 -O0 -g——-O2 会让变量被寄存器化、循环被展开,单步断点跳得很乱。
3.2 gdb 常用命令
1
2
3
4
5
6
7
8
9
10
11
| gdb ./debug_demo
(gdb) break main # 在 main 打断点
(gdb) run # 启动
(gdb) next # 单步(不进入函数)
(gdb) step # 单步(进入函数)
(gdb) print var # 打印变量
(gdb) info locals # 打印所有局部变量
(gdb) backtrace # 函数调用栈
(gdb) continue # 继续执行
(gdb) quit
|
3.3 段错误定位
1
2
3
4
5
6
7
8
9
10
11
| # core dump 开启
ulimit -c unlimited
echo "/tmp/core.%e.%p" | sudo tee /proc/sys/kernel/core_pattern
# 编译
g++ -g -o bug bug.cpp
./bug
# Segmentation fault (core dumped)
gdb ./bug /tmp/core.bug.12345
(gdb) backtrace
|
vim + gdb 联动:装 clewn 或 vimgdb,可以在 vim 里直接 F8 调出 gdb 控制台、跳转到对应行。
四、Makefile:从源码到产物
裸 g++ 命令一多就乱——头文件改一个,整个项目得全部重编。make 是 C/C++ 项目最朴素的构建系统。
4.1 一个 Makefile 模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| # 编译器
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -O2 -g
LDFLAGS =
# 源文件、目标文件
SRCS = $(wildcard src/*.cpp)
OBJS = $(SRCS:.cpp=.o)
TARGET = bin/myapp
# 默认目标
all: $(TARGET)
$(TARGET): $(OBJS)
@mkdir -p bin
$(CXX) $(OBJS) -o $@ $(LDFLAGS)
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
# 头文件依赖
deps:
$(CXX) -MMD -MP $(CXXFLAGS) $(SRCS) > /dev/null
# 包含自动生成的 .d 文件
-include $(OBJS:.o=.d)
clean:
rm -f src/*.o src/*.d
rm -rf bin
.PHONY: all clean deps
|
关键技巧:
wildcard src/*.cpp 自动收集源文件,新增文件不用改 Makefile%.o: %.cpp 模式规则,每个 .cpp 对应一个 .o-MMD -MP 自动追踪头文件依赖:改 foo.h 自动重编 bar.cppbin/ 目录 @mkdir -p 懒创建
4.2 并行编译
1
2
| make -j$(nproc) # Linux
make -j$(sysctl -n hw.ncpu) # macOS
|
多核 CPU 能让构建时间从分钟级压到秒级。
五、CMake:跨平台构建
make 写多了会失控——一个项目几百个源文件、不同平台编译器不同、依赖第三方库路径各异。CMake 是事实标准:写一份 CMakeLists.txt,生成 Makefile / Ninja / Xcode / Visual Studio 工程。
5.1 最简单的 CMakeLists
1
2
3
4
5
6
7
| cmake_minimum_required(VERSION 3.10)
project(myapp CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(myapp main.cpp foo.cpp bar.cpp)
|
1
2
3
| mkdir build && cd build
cmake .. # 生成 Makefile
make -j$(nproc) # 编译
|
5.2 找第三方库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| find_package(Boost REQUIRED COMPONENTS filesystem system)
find_package(OpenCV REQUIRED)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SQLITE3 REQUIRED sqlite3)
add_executable(myapp main.cpp)
target_include_directories(myapp PRIVATE ${Boost_INCLUDE_DIRS})
target_link_libraries(myapp
PRIVATE
Boost::filesystem
Boost::system
${OpenCV_LIBS}
${SQLITE3_LIBRARIES}
)
|
5.3 现代化 CMake(3.5+ 推荐)
老派 CMake 用全局 include_directories / link_libraries 会污染所有 target,新写法是 target_* + PRIVATE/PUBLIC/INTERFACE 限定作用域:
1
2
3
4
5
6
| add_library(mylib STATIC src/foo.cpp)
target_include_directories(mylib PUBLIC include)
target_compile_features(mylib PUBLIC cxx_std_17)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE mylib)
|
六、常见坑
| 坑 | 现象 | 解法 |
|---|
undefined reference to std::cout | 用了 gcc 编译 .cpp | 改用 g++ |
| 模板类找不到 | linker 报缺失符号 | 把模板实现放 .hpp(同文件),或显式实例化 |
multiple definition of ... | 头文件有非 inline 函数/变量 | 函数加 inline、变量加 inline(C++17)或 extern |
| 段错误 | 数组越界 / 空指针 / 栈溢出 | gdb + valgrind --tool=memcheck ./myapp |
| 编译极慢 | 单个翻译单元 1 万+ 行 | 用 #include 拆分、前向声明、Pimpl 模式 |
链接 .so 报 cannot find -lxxx | 库不在标准路径 | -L/path/to/lib -lxxx 或 LD_LIBRARY_PATH |
七、下一步
- 工具链进阶:
clangd + clang-tidy 静态分析、include-what-you-use 删无用头文件 - 构建系统:
Ninja 比 make 增量构建快 30%+,cmake -G Ninja .. - 包管理:
vcpkg(微软)、Conan(C++ 生态最主流)安装第三方库 - 测试:
GoogleTest(gtest)+ gcovr 看覆盖率 - 现代 C++:C++20 的 concept / coroutine / module,C++23 的
expected / flat_map
参考资料
2024+ 视角:C++20 / 23 / 26 与现代工具链
C++ 标准演进(2024 视角)
| 标准 | 发布时间 | 状态 | 编译器支持(2024) |
|---|
| C++17 | 2017 | 主流 | GCC 7+ / Clang 5+ / MSVC 19.14+ |
| C++20 | 2020 | 生产可用 | GCC 10+ / Clang 13+ / MSVC 19.28+ |
| C++23 | 2024-02 | 新晋主流 | GCC 12+ / Clang 16+ / MSVC 19.34+ |
| C++26 | 2026(计划) | 起草中 | 实验分支 |
C++20 必学特性
- Concepts:模板约束,写法
template<std::integral T> - Modules(
import std;):告别头文件包含地狱——大型项目编译时间降 50%+ - Coroutines(
co_await / co_return):异步代码同步写法 - Ranges(
<ranges>):管道式数据处理 nums \| std::views::filter([](int x){ return x%2==0; }) std::span:非拥有式数组视图,零拷贝传递std::format:类型安全的字符串格式化(Python f-string 风格)
1
2
3
4
5
6
7
8
9
| // C++20 concept 示例
#include <concepts>
template<std::integral T>
T add(T a, T b) { return a + b; }
// 使用
int x = add(1, 2); // OK
// double y = add(1.0, 2.0); // 编译错误:double 不是 integral
|
C++23 必学特性
std::expected<T, E>:替代异常或错误码的"成功或失败"类型std::flat_map / std::flat_set:基于连续内存的 map/set——缓存友好,比红黑树快 2-3 倍std::mdspan:多维数组视图——机器学习 / 图像处理天然支持std::print / std::println:类型安全的 print 替代 std::cout <<deducing this(显式对象形参):CRTP 不再需要复杂模板
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| // C++23 std::expected
#include <expected>
#include <string>
std::expected<int, std::string> parseInt(std::string_view s) {
try {
return std::stoi(std::string(s));
} catch (...) {
return std::unexpected("parse failed: " + std::string(s));
}
}
auto result = parseInt("42");
if (result) {
std::println("got: {}", *result);
} else {
std::println("error: {}", result.error());
}
|
C++26 路线图(草案)
- Reflection(静态反射):编译器生成枚举字符串、字段列表
- Contracts(契约):
[[expects: x > 0]] / [[ensures: result > 0]] std::execution(P2300):标准化的异步执行器,senders/receivers- Pattern Matching(
inspect):函数式语言风格的模式匹配
编译工具链的 2024+ 演进
GCC 14 / Clang 18 / MSVC 19.40+:
- GCC 14:完整 C++23 支持
- Clang 18:默认 C++17,可选 C++20/23
- MSVC:与 LLVM 社区代码同步,差异越来越小
跨平台推荐:
1
2
3
4
5
6
7
| # Ubuntu 24.04 LTS 默认装 GCC 13
sudo apt install g++-14 # 装 GCC 14
# macOS
brew install llvm@18 # 装 Clang 18
# Windows:MSYS2 / vcpkg / WSL2
|
构建系统的 2024+ 现状
| 工具 | 状态 | 2024+ 推荐 |
|---|
| CMake 3.28+ | 主流 | 首选(Presets 3.27+ 增强) |
| Ninja 1.12+ | 主流 | 比 Make 快 30%+,CMake -G Ninja |
| Bazel 7.x | Google 系 / 跨语言 | 大型项目 + 远程缓存 |
| Meson 1.5+ | Python 写 | 简单 / 中型项目 |
| xmake | Lua 写 | 国内友好(tboox 维护) |
| Make | 老派 | 维护遗留项目 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| # CMakePresets.json(2024+ 标准做法)
{
"version": 6,
"configurePresets": [
{
"name": "base",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_CXX_STANDARD": "23",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
}
},
{
"name": "debug",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
}
]
}
|
1
2
3
| # 一行配置
cmake --preset debug
cmake --build build/debug
|
包管理的 2024+ 现状
| 工具 | 特点 | 2024+ |
|---|
| vcpkg(微软) | 3000+ 包,跨平台 | 生产首选 |
| Conan 2.x | C++ 生态最成熟 | 复杂项目 + 二进制分发 |
| Spack | HPC / 科研 | 学术 / 超级计算 |
| CPM.cmake | CMake 内置 | 简单项目(FetchContent 替代) |
| xrepo(xmake 系) | 跨平台 | 国内项目 |
1
2
3
4
5
6
7
8
9
10
| # vcpkg 集成(2024+)
# vcpkg.json
{
"name": "my-project",
"version-string": "1.0.0",
"dependencies": [
"fmt", "spdlog", "boost-system", "boost-filesystem",
{ "name": "qt", "features": ["webengine"] }
]
}
|
调试工具的 2024+ 现状
- gdb 14+:原生支持 C++20 coroutine 调试
- lldb 18+:Clang 配套,符号信息更准
- rr(Mozilla 录制回放):5.x 稳定——重现 bug 一把好手
- AddressSanitizer / UBSan / ThreadSanitizer:编译器内置,2024+ 必开
- Valgrind 3.23+:老牌但仍可用
- Visual Studio Code + CodeLLDB:调试 C++ 的"轻量 IDE"
IDE 的 2024+ 推荐
- CLion 2024.2+:JetBrains 系,对 CMake / vcpkg 集成最好(付费)
- Visual Studio 2022 17.10+:Windows + C++23 支持最完整
- VS Code + CMake Tools + clangd:免费 + 轻量,Linux/macOS 首选
- Qt Creator 13+:跨平台 + Qt 项目专用
- Zed:2024 出现的新编辑器,Rust 写的,启动极快
2024+ 静态分析工具链
| 工具 | 用途 | 2024+ |
|---|
| clang-tidy 18+ | 风格 + bug 模式 | 必装 |
| cppcheck | 基础静态分析 | CI 必跑 |
| include-what-you-use | 检查无用 #include | 大型项目用 |
| SonarQube 10+ | 企业级代码质量平台 | 中大型 |
| PVS-Studio | 商业深度分析 | 金融 / 嵌入式 |
2024+ 推荐组合(跨平台 C++ 项目)
1
2
3
4
5
6
7
8
9
10
11
12
| 现代 C++ 项目(C++20/23)
├── 编译器:GCC 14 / Clang 18 / MSVC 19.40
├── 标准:C++20(生产)/ C++23(新项目)
├── 构建:CMake 3.28 + Ninja 1.12 + CMakePresets
├── 包管理:vcpkg(首选)/ Conan 2.x
├── 测试:GoogleTest + gcovr / LLVM-cov
├── 静态分析:clang-tidy + cppcheck
├── 格式化:clang-format 18
├── 调试:gdb 14 / lldb 18 + rr
├── IDE:VS Code + clangd / CLion
├── 编译选项:-Wall -Wextra -Wpedantic -fsanitize=address,undefined
└── CI:GitHub Actions / GitLab CI
|