C++ 编程规范:101条规则准则与最佳实践
引言
C++ 是一门强大而复杂的语言,能高效控制硬件,也能写出优雅抽象。然而,正因其复杂性,项目中若缺乏统一规范,极易陷入混乱、难维护、易出错的泥潭。
本文总结了 101条 C++ 编程规范与最佳实践,涵盖 命名、结构、内存管理、多线程、异常、安全、性能等多个维度,旨在帮助开发者构建高质量、可维护、可扩展的 C++ 项目。
一、命名与风格(Rules 1–10)
# |
规则 |
简要建议 |
1 |
类名使用大驼峰 PascalCase |
如 ThreadPool ,提高可辨识性 |
2 |
变量名使用小驼峰 camelCase |
例:logFilePath ,区分于类名 |
3 |
常量用 ALL_CAPS + _ 分隔 |
强化不变含义,如 MAX_BUFFER_SIZE |
4 |
命名需语义明确 |
避免 data , tmp ,使用 configFilePath 更清晰 |
5 |
缩写仅限通用缩写 |
如 idx , buf ,尽量使用全称提升可读性 |
6 |
函数名用动词 + 名词 |
例如 loadConfig() 表意清晰 |
7 |
命名空间用小写 |
如 network::socket ,避免歧义 |
8 |
接口类加 I 前缀(可选) |
如 ILogger ,强调为接口 |
9 |
成员变量加前缀/后缀 |
m_ 或 _ 表示成员变量,增强可读性 |
10 |
用 enum class 替代裸 enum |
强类型更安全,防止命名冲突 |
二、代码结构与风格(Rules 11–20)
# |
规则 |
简要建议 |
11 |
每个头文件只声明一个模块 |
避免多义性,便于编译与复用 |
12 |
使用 #pragma once 或 include guard |
防止重复包含 |
13 |
include 顺序:本地 > 第三方 > STL |
增强可读性与可维护性 |
14 |
避免头文件中包含过多实现 |
使用前向声明可减少依赖 |
15 |
类/函数应单一职责 |
有助于测试与扩展 |
16 |
控制函数长度 < 60 行 |
超过建议拆分子函数 |
17 |
控制每个文件长度 < 2000 行 |
模块化设计更清晰 |
18 |
每行不超过 120 字符 |
保证阅读体验,特别在 review 时 |
19 |
使用 4 空格缩进,禁止制表符 |
统一格式,防止跨平台混乱 |
20 |
所有控制结构都用 {} 包围 |
防止隐式逻辑错误,如单行 if 陷阱 |
三、类设计与对象管理(Rules 21–30)
# |
规则 |
建议 |
21 |
所有成员变量应为私有 |
使用 getter/setter 访问 |
22 |
提供合理构造/析构函数 |
保证资源初始化与释放对称 |
23 |
禁用复制/移动时应 = delete |
明确意图,防止误用 |
24 |
用 explicit 阻止隐式转换 |
如 explicit Config(std::string path) |
25 |
避免裸指针作为成员 |
使用 unique_ptr /shared_ptr 安全管理 |
26 |
构造函数不做复杂逻辑 |
仅初始化,不处理业务 |
27 |
基类析构函数应为 virtual |
否则 delete 派生类有 UB |
28 |
优先使用组合而非继承 |
组合更灵活、低耦合 |
29 |
不使用多重继承(除非纯接口) |
降低复杂度,避免菱形继承问题 |
30 |
避免深层继承结构 |
建议控制在 2 层以内 |
四、函数与模板(Rules 31–40)
# |
规则 |
建议 |
31 |
参数多于 3 个建议封装结构体 |
提高可读性与扩展性 |
32 |
参数传递规则明确 |
小型值传递,大型对象引用 |
33 |
函数返回值推荐智能指针或值传递 |
避免裸指针和资源泄露 |
34 |
函数要写用途注释 |
特别是公共接口或库函数 |
35 |
模板逻辑应轻量,避免过多嵌套 |
编译时间压力大时尤需注意 |
36 |
合理使用 auto 简化类型 |
不影响语义的地方使用 |
37 |
模板中加入 static_assert 限定 |
增强类型安全性 |
38 |
使用 constexpr 提升编译期能力 |
如常量计算函数 |
39 |
控制模板递归深度 |
编译器对深层模板支持有限 |
40 |
模板尽可能放 header 中定义 |
避免链接错误(ODR 问题) |
五、内存管理(Rules 41–50)
# |
规则 |
建议 |
41 |
禁止裸 new/delete |
用 make_unique /make_shared 替代 |
42 |
所有资源管理用 RAII |
让析构自动释放资源 |
43 |
禁止手动 free/close |
封装在类中自动释放 |
44 |
指针拥有权应清晰 |
避免 ownership 混乱 |
45 |
避免 shared_ptr 在多线程竞争 |
使用 atomic_shared_ptr 或避免频繁共享 |
46 |
使用工具检测泄漏 |
如 Valgrind、ASan |
47 |
使用智能指针区分 shared/unique 语义 |
更清晰,更安全 |
48 |
不要传值传递 shared_ptr |
用 const& 降低引用计数开销 |
49 |
使用容器代替裸数组 |
STL 容器更安全 |
50 |
类封装资源释放逻辑 |
遵守 RAII,职责清晰 |
六、异常处理与错误传递(Rules 51–60)
# |
规则 |
建议 |
51 |
尽量避免使用异常 |
推荐 error code / Result<T> 结构 |
52 |
异常必须 catch 并处理 |
记录日志,避免 silent fail |
53 |
不使用 catch (...) |
易隐藏逻辑错误 |
54 |
构造函数中不抛异常 |
否则无法确定对象是否成功创建 |
55 |
明确错误处理模块 |
集中统一处理错误 |
56 |
注释中注明错误返回 |
增强调用方对异常的理解 |
57 |
编写无副作用函数 |
降低调试/测试成本 |
58 |
日志输出必须有上下文 |
包括文件名/函数名/线程信息 |
59 |
接口错误向上传递 |
不要在底层吞掉问题 |
60 |
异常路径不得影响主逻辑性能 |
异常处理应轻量快捷 |
七、多线程与并发(Rules 61–70)
# |
规则 |
建议 |
61 |
封装线程操作 |
避免裸用 std::thread |
62 |
原子操作使用 std::atomic |
避免竞态条件 |
63 |
使用细粒度锁或无锁结构 |
提升性能,减少死锁 |
64 |
使用 lock_guard 管理锁 |
自动加锁释放 |
65 |
不捕获局部引用传入线程 |
否则线程中变量悬空 |
66 |
避免死锁 |
控制锁顺序,使用 std::scoped_lock |
67 |
避免全局变量并发读写 |
用线程局部存储或加锁保护 |
68 |
构建线程池封装并发任务 |
避免线程爆炸与资源浪费 |
69 |
不得在对象析构前 detach 线程 |
否则存在野线程 |
70 |
使用条件变量控制等待 |
避免忙等浪费 CPU |
八、性能优化(Rules 71–80)
# |
规则 |
建议 |
71 |
优化热点路径 |
代码中使用 likely / unlikely |
72 |
用 std::move 转移资源 |
防止不必要的拷贝 |
73 |
使用 emplace_back |
避免对象额外构造拷贝 |
74 |
使用 reserve 预分配空间 |
降低 reallocation 成本 |
75 |
避免频繁申请释放内存 |
推荐对象池或内存复用 |
76 |
避免虚函数热路径中使用 |
可用策略模式等替代 |
77 |
小函数可使用 inline |
减少函数调用开销 |
78 |
注意 ABI 兼容性 |
跨平台或多版本部署需考虑 |
79 |
无序容器快于有序容器 |
unordered_map 通常优于 map |
80 |
使用 string_view 避免拷贝 |
尤其在字符串解析场景中 |
九、安全与健壮性(Rules 81–90)
# |
规则 |
建议 |
81 |
所有输入必须校验合法性 |
防止越界、注入等问题 |
82 |
检查整数溢出风险 |
使用安全加法函数 |
83 |
禁止数组越界访问 |
用 at() 或容器封装 |
84 |
使用 RAII 管理资源 |
防止内存泄漏或悬空指针 |
85 |
IO 操作必须检查返回值 |
否则容易逻辑错误 |
86 |
不在库中使用 exit/abort |
破坏调用者行为 |
87 |
库中不处理 UI/日志 |
由上层决定策略 |
88 |
接口遵循最小权限原则 |
降低攻击面与耦合 |
89 |
使用静态分析工具辅助检查 |
如 clang-analyzer, cppcheck |
90 |
禁止未定义行为写法 |
避免 UB 问题,如越界指针、悬空引用等 |
十、工程实践与工具链(Rules 91–101)
# |
规则 |
建议 |
91 |
接入持续集成(CI) |
自动编译与检查保障质量 |
92 |
使用单元测试框架 |
推荐 GTest/GMock |
93 |
使用代码覆盖率工具 |
识别未测试路径 |
94 |
强制统一代码格式化工具 |
推荐 clang-format |
95 |
接入内存检测工具 |
如 AddressSanitizer |
96 |
使用 CMake 管理构建 |
跨平台统一构建系统 |
97 |
单元测试覆盖率 >= 80% |
提升可靠性 |
98 |
所有代码需 Code Review |
防止低级错误 |
99 |
接入日志和监控模块 |
如 Prometheus、Grafana |
100 |
所有模块应可独立构建测试 |
降低耦合度 |
101 |
每半年重审一次规范 |
适应团队与项目演化 |