目录
一、命名规范
1. 通用原则
2. 具体命名规则
二、代码格式规范
1. 缩进与换行
2. 括号与空格
3. 空行与注释
三、语法与编码规范
1. 头文件与包含
2. 类型与变量
3. 函数与类
4. 内存管理
5. 控制语句
四、注释规范
1. 文档注释(Doxygen 风格)
2. 代码内注释
五、其他重要规范
六、工具保障
七、文件组织与结构
1. 文件命名与后缀
2. 头文件结构
3. 源文件结构
八、命名规范(补充细节)
1. 特殊场景命名
2. 避免的命名
九、代码格式(补充细节)
1. 换行规则
2. 空格使用细节
3. 注释格式
十、语法与编码规范(补充细节)
1. 类型使用
2. 变量与初始化
3. 函数设计
4. 类与面向对象
5. 内存与资源管理
6. 控制流
十一、异常与错误处理
1. 异常使用规范
2. 错误码场景
十二、模板与泛型编程
十三、性能与安全性
1. 性能优化
2. 安全性措施
十四、工具与自动化保障
十五、特殊场景处理
C++ 代码规范是保证大型项目可维护性、可读性和一致性的基础,尤其在团队协作中至关重要。以下是一套全面的 C++ 代码规范,涵盖命名、格式、语法、设计等多个维度:
一、命名规范
1. 通用原则
命名需清晰表达含义,避免缩写(除非是广为人知的缩写如
、
CPU
)禁止使用拼音或拼音与英文混合命名命名长度适中,既不冗余也不晦涩
JSON
2. 具体命名规则
元素类型 | 命名风格 | 示例 |
---|---|---|
变量 / 函数参数 | 小写字母 + 下划线(蛇形) | ,
|
全局变量 | 前缀 + 蛇形 |
,
|
类 / 结构体 / 枚举 | 首字母大写(帕斯卡) | ,
|
类成员变量 | 蛇形 + 后缀
|
,
|
函数 / 方法 | 首字母大写(帕斯卡) | ,
|
常量 / 宏定义 | 全大写 + 下划线 | ,
|
枚举值 | 全大写 + 下划线 | ,
|
命名空间 | 小写字母 + 下划线 | ,
|
模板参数 | 大写单字母或帕斯卡 | ,
|
二、代码格式规范
1. 缩进与换行
使用 4 个空格缩进(禁止使用制表符
)每行代码长度不超过 80-120 字符(避免横向滚动)函数参数过多时,每行一个参数并对齐
Tab
void ComplexFunction(int param1,
const std::string& param2,
bool param3 = false) {
// 函数体
}
2. 括号与空格
左括号
不单独成行,紧跟在语句后
{
if (condition) { // 正确
// 代码
}
if (condition)
{ // 错误
// 代码
}
运算符前后加空格:
(非
a + b
),
a+b
(非
x = 5
)逗号后加空格:
x=5
(非
func(a, b, c)
)括号内侧不加空格:
func(a,b,c)
(非
if (x > 0)
)
if ( x > 0 )
3. 空行与注释
逻辑块之间加空行分隔(如函数内不同功能段)文件末尾保留一个空行单行注释与代码之间至少隔一个空格
int result = a + b; // 计算总和(正确)
int result=a+b;//计算总和(错误)
三、语法与编码规范
1. 头文件与包含
头文件使用
防止重复包含(优先于 #ifndef 方式)包含顺序:标准库 → 第三方库 → 项目内头文件(用空行分隔)
#pragma once
#include <iostream>
#include <vector>
#include <boost/asio.hpp>
#include "utils/logger.h"
#include "network/socket.h"
禁止在头文件中定义变量或实现非内联函数
2. 类型与变量
优先使用 C++11 + 类型:
(非
int32_t
)、
int
(非
size_t
)初始化变量时避免 “神秘数字”,使用常量替代
unsigned int
const int MAX_RETRY = 3; // 正确
for (int i = 0; i < MAX_RETRY; ++i) { ... }
for (int i = 0; i < 3; ++i) { ... } // 错误(3的含义不明确)
禁止使用全局变量(必须使用时需加
前缀并文档说明)指针 / 引用符号贴近变量名:
g_
(非
int* ptr
)
int *ptr
3. 函数与类
函数参数优先使用
引用传递大对象(避免拷贝开销)
const
void PrintData(const std::vector<int>& data); // 正确
void PrintData(std::vector<int> data); // 错误(不必要的拷贝)
类成员函数:不修改成员变量时加
const
int GetAge() const { return age_; } // 正确
析构函数必须是
(基类)或
virtual
(派生类)禁止在构造函数 / 析构函数中调用虚函数
override
4. 内存管理
优先使用智能指针(
/
std::unique_ptr
),禁止裸指针
std::shared_ptr
/
new
使用
delete
而非
nullptr
或
NULL
容器使用:
0
替代 C 风格数组,
std::vector
替代
std::string
char*
5. 控制语句
/
if-else
/
for
语句即使只有一行代码也必须加括号
while
if (condition) { // 正确
DoSomething();
}
if (condition) DoSomething(); // 错误
避免深层嵌套(建议不超过 3 层),可拆分函数简化
语句必须包含
switch
分支,
default
块需用
case
终止(或显式注释穿透)
break
四、注释规范
1. 文档注释(Doxygen 风格)
类 / 函数 / 常量定义前必须加文档注释,说明功能、参数、返回值
/**
* @brief 从文件读取数据并解析
* @param file_path 输入文件路径(绝对路径)
* @param[out] result 解析后的数据存储容器
* @return 成功返回true,失败返回false
* @note 支持的文件格式:.txt, .csv
*/
bool ReadAndParse(const std::string& file_path, std::vector<Data>& result);
2. 代码内注释
复杂逻辑处必须加注释说明 “为什么这么做”(而非 “做了什么”)避免冗余注释(如
)临时注释(如
i++;//i自增1
)需及时处理
// TODO: 优化算法
五、其他重要规范
错误处理:使用异常(
/
throw
)而非错误码,关键操作必须检查返回值跨平台兼容:避免平台特定 API(如 Windows 的
try-catch
),使用抽象层封装性能考量:
CreateFile
避免在循环内创建临时对象大容器遍历优先使用
频繁调用的小函数加
const_iterator
安全性:
inline
禁止使用
/
gets
等不安全函数,改用
sprintf
/
fgets
输入数据必须验证(防止缓冲区溢出、注入攻击)
snprintf
六、工具保障
使用
自动格式化代码(配置文件统一团队风格)集成
Clang Format
/
Clang Tidy
进行静态检查代码审查(PR/MR)时强制检查规范符合性
Cppcheck
遵循规范的核心目标是 “让代码读起来像自然语言”,减少团队协作中的理解成本。规范需根据项目实际情况灵活调整,并通过自动化工具尽可能降低执行成本。
七、文件组织与结构
1. 文件命名与后缀
源文件:小写蛇形命名,
后缀(如
.cpp
)头文件:小写蛇形命名,
file_processor.cpp
后缀(如
.h
)模板实现文件:
file_processor.h
后缀(如
.tpp
),仅在头文件末尾包含禁止文件名包含空格、特殊字符或版本号(如
vector_utils.tpp
)
data_handler_v2.cpp
2. 头文件结构
强制使用
作为防重复包含机制(比
#pragma once
更高效)头文件必须包含的内容:
#ifndef
版权声明(顶部,团队 / 公司统一格式)简要功能描述(文件级注释)包含必要的头文件(不冗余、不缺失)命名空间包裹的声明 禁止内容:
using namespace 指令(避免命名污染)非内联函数实现全局变量定义(可声明
)复杂表达式或执行语句
extern
3. 源文件结构
结构顺序:
版权声明包含头文件(按标准库→第三方库→项目内的顺序)全局常量 / 静态变量定义命名空间函数实现(先非成员函数,后类成员函数)类模板 / 函数模板实现(如需)
八、命名规范(补充细节)
1. 特殊场景命名
私有成员函数:前缀
(如
Private
)回调函数:后缀
PrivateCalculateHash()
(如
Callback
)测试函数:
ConnectionTimeoutCallback()
+ 功能描述(如
Test
)临时变量:短命名需有明确含义(如
TestFileReader_EmptyFile()
用于循环索引,
i
用于临时字符串)布尔变量:前缀
tmp_str
/
is_
/
has_
(如
can_
、
is_valid
)
has_permission
2. 避免的命名
与 C++ 关键字冲突(如
、
class
)单字母命名(除循环索引
template
/
i
、模板参数
j
等约定场景)含义模糊的命名(如
T
、
process()
、
handle()
)与标准库重名(如
data
、
vector
)
list
九、代码格式(补充细节)
1. 换行规则
运算符处换行时,运算符位于新行开头:
// 正确
const int total = first_value
+ second_value
- adjustment;
// 错误
const int total = first_value +
second_value -
adjustment;
模板参数过长时换行对齐:
std::unordered_map<std::string,
std::vector<std::pair<int, float>>>
complex_map;
2. 空格使用细节
范围 for 循环的冒号前后加空格:
for (const auto& item : container) { ... } // 正确
for(const auto& item:container) { ... } // 错误
指针 / 引用与类型、变量的空格:
int* ptr; // 正确(*贴近变量)
const std::string& name; // 正确(&贴近类型)
int * ptr; // 错误
函数指针声明空格:
void (*callback)(int); // 正确
void(*callback)(int); // 错误
3. 注释格式
块注释使用
(Doxygen 风格),单行注释使用
/** ... */
注释与代码对齐:
//
int width = 1024; // 窗口宽度(像素)
int height = 768; // 窗口高度(像素)
多行注释缩进与代码一致:
// 这是一个多行注释示例
// 说明该函数的特殊处理逻辑
// 1. 首先验证输入
// 2. 然后执行转换
void TransformData(...) { ... }
十、语法与编码规范(补充细节)
1. 类型使用
整数类型:
明确大小:
/
int8_t
(8 位)、
uint8_t
/
int32_t
(32 位)等计数 / 索引:优先
uint32_t
(无符号)禁止使用
size_t
(在不同平台大小不一致) 浮点类型:优先
long
(除非明确需要
double
节省空间)字符串:
float
,避免
std::string
;多行字符串使用
char*
原始字符串字面量
R"(...)"
2. 变量与初始化
初始化方式:
内置类型:
(而非
int count = 0;
)类对象:
int count; count = 0;
(列表初始化,防止窄化转换) 变量声明位置:就近原则(在首次使用前声明,避免函数开头集中声明)全局变量:禁止使用(替代方案:单例模式、局部静态变量)静态变量:仅在函数内或匿名命名空间中使用(避免跨编译单元依赖)
User user{"Alice", 25};
3. 函数设计
参数顺序:输入参数在前,输出参数在后(指针 / 引用标记
)
[out]
bool ProcessData(const Input& input, // [in]
Output* output); // [out]
默认参数:仅放在函数声明中(头文件),不重复放在定义中返回值:
禁止返回局部变量的引用 / 指针复杂类型返回时用
避免拷贝布尔返回值函数名应体现判断结果(如
return std::move(value);
而非
IsValid()
)
CheckValid()
4. 类与面向对象
类声明顺序(访问控制从高到低):
(构造函数、析构函数、接口方法)
public
(保护成员、方法)
protected
(私有成员、方法) 继承:
private
公有继承表示 “是一种”(IS-A)关系,私有继承表示 “实现借助于”派生类析构函数必须用
(禁止
override
重复声明)禁止多重继承(除非其中一个是接口类) 构造函数:
virtual
单参数构造函数必须加
(防止隐式转换)
explicit
禁止在构造函数中执行复杂逻辑(移至
方法)
Init()
explicit FileReader(const std::string& path); // 正确
FileReader(const std::string& path); // 错误(可能隐式转换)
5. 内存与资源管理
智能指针使用场景:
唯一所有权:
(默认首选)共享所有权:
std::unique_ptr
(谨慎使用,避免循环引用)弱引用:
std::shared_ptr
(配合
std::weak_ptr
解决循环引用) 容器使用:
shared_ptr
初始化:
(列表初始化)迭代:
std::vector<int> nums{1, 2, 3};
(范围 for 循环)清空:
for (const auto& num : nums) { ... }
而非重新创建对象 禁止操作:
nums.clear();
(导致未定义行为)裸指针算术运算(改用
delete this
索引)
std::vector
(除非与硬件 / 系统 API 交互)
reinterpret_cast
6. 控制流
语句:
if
条件表达式避免赋值操作:
(易与
if (a = b)
混淆)多条件用逻辑运算符连接,避免嵌套:
==
// 正确
if (condition1 && condition2 && condition3) { ... }
// 错误(深层嵌套)
if (condition1) {
if (condition2) {
if (condition3) { ... }
}
}
语句:
switch
标签缩进与
case
一致:
switch
switch (type) {
case TYPE_A:
HandleA();
break;
case TYPE_B:
HandleB();
break;
default:
HandleUnknown();
}
需穿透的
必须加注释:
case
case TYPE_X:
HandleX();
// 穿透到TYPE_Y处理
case TYPE_Y:
HandleY();
break;
循环:
禁止死循环(必须有明确退出条件)避免
(仅允许在函数内跳出多层循环)
goto
十一、异常与错误处理
1. 异常使用规范
异常类型:
自定义异常需继承
按错误类型细分异常类(如
std::exception
、
FileNotFoundException
) 抛出时机:
InvalidArgumentException
错误不可恢复时抛出(如文件不存在、内存分配失败)抛出描述性信息:
捕获原则:
throw FileError("Failed to open: " + path);
只捕获能处理的异常(不滥用
)捕获时用
catch (...)
引用:
const
捕获后要么处理,要么重新抛出(
catch (const FileError& e)
)
throw;
2. 错误码场景
仅在不允许抛出异常的场景使用(如实时系统、内核代码)错误码定义为枚举或常量,避免魔数:
enum class ErrorCode {
kSuccess = 0,
kInvalidInput = 1,
kNetworkError = 2
};
十二、模板与泛型编程
模板参数命名:
单个字母:
(类型)、
T
(数值)描述性名称:
N
、
ElementType
模板约束:C++20 起使用
Policy
限制模板参数(避免晦涩错误)实现位置:模板定义与实现必须在同一翻译单元(通常放在头文件)
concepts
十三、性能与安全性
1. 性能优化
避免不必要的拷贝:
传参:大对象用
,可修改对象用
const&
(移动语义)返回:
&&
循环优化:
return std::move(local_obj);
减少循环内计算(将不变量移至循环外)连续内存访问(利用 CPU 缓存) 避免隐式转换:尤其是数值类型间的窄化转换(如
→
double
)
int
2. 安全性措施
输入验证:所有外部输入(文件、网络、用户输入)必须验证合法性缓冲区安全:使用
/
std::string
替代
std::vector
,避免
char[]
等函数线程安全:
strcpy
共享数据加锁(
)避免在多线程中使用非线程安全对象(如
std::mutex
需加锁) 资源释放:使用 RAII 模式(如
std::cout
、自定义资源管理器)
std::lock_guard
十四、工具与自动化保障
格式化工具:
(配置文件
Clang Format
统一团队风格)静态检查:
.clang-format
(代码风格、潜在错误)
Clang Tidy
(空指针、内存泄漏)
Cppcheck
(运行时内存检查) 代码审查 checklist:
Valgrind
命名是否符合规范注释是否完整清晰是否使用智能指针 / RAII是否有异常处理单元测试覆盖率是否达标
十五、特殊场景处理
跨平台代码:
平台相关代码用条件编译隔离:
#ifdef _WIN32
// Windows特定实现
#elif __linux__
// Linux特定实现
#else
#error "Unsupported platform"
#endif
路径分隔符使用
等常量(避免直接写
kPathSeparator
或
/
) 日志输出:
禁止在生产代码中使用
/
std::cout
(改用日志库)日志级别明确:
printf
/
DEBUG
/
INFO
/
WARN
/
ERROR
FATAL
这套规范的核心原则是 “清晰、一致、安全、可维护”。实际项目中,可根据团队规模、项目类型(嵌入式 / 桌面 / 服务端)进行适当裁剪,但需确保团队全员严格遵守同一套标准,并通过自动化工具最大程度减少人工检查成本。