这份规范聚焦工程化C/C++开发,核心目标是提升代码可读性、可维护性,促进团队协作,同时通过标准化注释实现文档自动生成。规范覆盖头文件、类/结构体、代码格式、命名约定、Doxygen注释五大核心维度,兼顾语法规范和工程实践,是团队协作开发的重要参考准则,以下为核心内容的系统整理与解读。
一、头文件规则(工程化开发核心)
头文件是C/C++模块化开发的基础,本规范重点解决多重包含、过度依赖、命名空间冲突三大问题,同时明确头文件与源文件的对应关系。
1.1 #define 防止多重包含(强制要求)
- 核心要求:所有头文件必须通过
#ifndef/#define/#endif实现防止单次编译过程中的多重包含(注:多个.cpp文件包含同一头文件是合法的,此规则仅限制单文件编译期)。 - 命名格式:
- 业务逻辑类头文件:直接以大写文件名+_H命名,如
x_msg_task.h对应X_MSG_TASK_H; - 类库/公用库头文件:在前面添加项目名称,避免跨项目命名冲突;
- 业务逻辑类头文件:直接以大写文件名+_H命名,如
- 关键禁忌:头文件中禁止做变量/函数定义,仅允许声明(定义会导致多文件包含时的重定义错误);
- 示例:
#ifndef X_MSG_TASK_H #define X_MSG_TASK_H // 仅写声明代码(函数声明、宏定义、结构体/类声明等) #endif
1.2 减少头文件依赖(性能优化重点)
- 核心原则:使用前置声明(forward declarations) 尽量减少头文件中
#include的数量,降低编译耦合度; - 原理:头文件的每一次
#include都会引入新的依赖,被依赖头文件的任何修改,都会导致所有包含该头文件的代码重新编译,增加编译成本; - 前置声明使用场景:
- 仅使用类/结构体的指针/引用时,无需包含头文件,直接前置声明即可,如
class XThread;、struct XData;;
- 仅使用类/结构体的指针/引用时,无需包含头文件,直接前置声明即可,如
- 必须包含头文件的场景:
- 定义类/结构体的实体对象(非指针/引用)、继承该类、调用该类的成员函数时,必须包含对应头文件;
- 通用准则:头文件中尽量不引用其他头文件,所有头文件引用移至.cpp源文件中。
1.3 头文件中不导入命名空间(避免冲突)
- 核心要求:头文件中禁止使用
using namespace xxx导入命名空间; - 正确用法:使用命名空间内的标识符时,直接加命名空间前缀,如
std::string str;、std::vector<int> vec;; - 命名空间导入位置:仅在cpp源文件中使用
using namespace,简化代码; - 原因:头文件会被其他未知文件包含,导入命名空间可能导致变量/函数命名冲突,引发不可预期的编译错误。
头文件与源文件的对应关系
- 通用规则:一个.cpp文件对应一个.h文件;
- 例外场景:
- 包含
main函数的入口文件,无对应的.h文件; - 仅含纯虚函数的接口类,只有.h文件,无对应的.cpp文件(无实现代码)。
- 包含
二、类/结构体规则(C++专属,兼顾C结构体)
本部分主要针对C++开发,明确类、结构体、继承、接口的使用规范,同时区分C/C++中结构体的使用场景,核心原则是清晰划分职责,降低设计复杂度。
2.1 构造函数的职责
- 核心要求:构造函数仅做无业务逻辑的初始化(如成员变量赋默认值、指针置空);
- 业务逻辑初始化:所有涉及业务逻辑的初始化操作,统一放在**
Init()方法**中集中实现; - 原因:构造函数无法返回错误码,若初始化失败难以处理;
Init()方法可通过返回值标识初始化结果,提升代码健壮性。
2.2 结构体(Struct)与类(Class)的区分
C语言场景
struct仅用于封装纯数据,无成员函数,是数据的集合。
C++场景
- Struct:仅当封装纯数据(无成员函数、无业务逻辑)时使用,成员默认访问权限为
public; - Class:除纯数据封装外的所有场景均使用class,成员默认访问权限为
private,支持封装、继承、多态; - 命名规则:类和结构体的成员变量使用不同的命名规则(详见本规范「变量命名」部分)。
2.3 继承(Inheritance)
- 核心原则:优先使用组合(Composition),而非继承;组合更灵活,降低代码耦合度;
- 继承的使用要求:若必须使用继承,仅允许public继承,禁止使用protected/private继承;
- 原因:protected/private继承会导致基类与派生类的耦合度过高,破坏封装性,且代码可读性差。
2.4 多重继承(Multiple Inheritance)
- 核心原则:尽量避免使用多重继承,多重继承易导致菱形继承、对象空间分配混乱等问题;
- 例外场景:若必须使用,需满足**「一实多虚」** 规则:
- 仅一个基类包含实际实现代码;
- 其他基类均为纯虚函数接口类(无成员变量、无实现代码)。
2.5 接口(Interface,C++专属)
- 接口的定义:满足以下条件的类即为接口,建议以
I开头命名(非强制);- 仅包含纯虚函数(
=0) 和静态函数; - 无任何成员变量;
- 无任何函数实现代码。
- 仅包含纯虚函数(
2.6 类内声明次序(强制规范)
为提升代码可读性,类内成员需按固定次序声明,且访问权限块按以下顺序排列:
public: → protected: → private:
每个访问权限块内的声明次序
- 构造函数;
- 析构函数;
- 成员函数(含静态成员函数,按功能相关性排列);
- 数据成员(含静态数据成员,按类型/用途排列)。
三、代码格式规则(团队协作基础)
统一的代码格式是团队协作的前提,本规范明确缩进、括号的标准格式,无歧义、易阅读。
3.1 缩进:仅使用空格,禁止制表位(Tab)
- 核心要求:全部使用空格实现缩进,禁止使用Tab键;
- 缩进标准:每次缩进4个空格;
- 原因:不同编辑器/终端对Tab的显示宽度不同(如2/4/8个空格),会导致代码格式错乱;空格缩进在所有环境下显示一致。
3.2 括号格式:左右括号单独起一行(Allman风格)
- 核心要求:条件语句(if/else)、循环语句(for/while/do)、函数定义的左右大括号均单独起一行;
- 格式示例:
// 条件语句 if (true) { // 代码块 } else { // 代码块 } // 函数定义 int Add(int a, int b) { return a + b; } // 循环语句 for (int i = 0; i < 10; i++) { // 代码块 } - 优势:代码层次清晰,大括号的匹配关系一目了然,便于调试和维护。
四、命名约定(核心重点,无歧义、强描述性)
命名是代码可读性的核心,本规范对文件、类/结构体/枚举、变量、函数、宏制定了严格的命名规则,核心原则是:具有描述性,不过度缩写;类型/变量用名词,函数用命令性动词。
4.1 通用命名规则(所有命名均需遵守)
- 命名需见名知意,具有强描述性,禁止使用无意义的缩写(如
a/b/c、tmp1/tmp2); - 类型(类/结构体/枚举/typedef)和变量用名词/名词短语命名;
- 函数用命令性动词/动词短语命名(如
Add、GetName、SaveData); - 禁止使用拼音与英文混合命名,统一使用英文。
4.2 文件命名
- 文件名全部小写;
- 单词之间用下划线(_) 分隔;
- 示例:
my_useful.cpp、x_msg_task.h、data_manager.h。
4.3 类型命名(类/结构体/枚举/typedef)
- 所有类型命名规则统一;
- 大驼峰命名法:每个单词的首字母大写,单词之间无下划线;
- 示例:
MyExcitingClass、UserInfo、UrlTableErrors、DataBuffer。
4.4 变量命名
- 小蛇形命名法:变量名一律小写,单词之间用下划线(_) 分隔;
- 类的成员变量:在变量名末尾加下划线(_),区分局部变量和成员变量;
- 局部变量/全局变量:直接使用小蛇形命名,无后缀;
- 示例:
// 局部变量 int my_exciting_local_variable; std::string user_name; // 类的成员变量 class MyClass { private: int num_entries_; std::string user_name_; };
4.5 函数命名
分普通函数和类的成员存取函数两种场景,分别制定规则。
普通函数(全局函数/类的普通成员函数)
- 大驼峰命名法:函数名首字母大写,每个单词首字母大写,无下划线;
- 用动词/动词短语命名,明确函数行为;
- 示例:
AddTableEntry()、GetUserInfo()、SaveFile()、InitConfig()。
类的成员存取函数(Get/Set函数)
- 为成员变量
xxx_提供的存取函数,统一使用小蛇形命名; - 获取函数:直接以成员变量名(去掉后缀_)命名,无
get前缀,返回对应值; - 设置函数:以
set_+成员变量名(去掉后缀_)命名,接收参数为待设置值; - 示例:
class MyClass { public: // 获取函数:num_entries_ → num_entries() int num_entries() const { return num_entries_; } // 设置函数:num_entries_ → set_num_entries() void set_num_entries(int num_entries) { num_entries_ = num_entries; } private: int num_entries_; };
4.6 宏、枚举常量命名
宏定义(#define)
- 全大写+下划线:所有字符大写,单词之间用下划线分隔;
- 示例:
#define PI_ROUNDED 3.0、#define MAX_BUFFER_SIZE 1024、#define ERROR_CODE -1。
枚举常量(enum内的常量)
- 全大写+下划线:与宏定义规则一致;
- 枚举类型本身:遵循「类型命名规则」(大驼峰);
- 示例:
// 枚举类型:大驼峰 enum UrlTableErrors { // 枚举常量:全大写+下划线 OK = 0, ERROR_OUT_OF_MEMORY, ERROR_MALFORMED_INPUT, };
五、Doxygen注释规则(自动生成文档,强制要求)
Doxygen是C/C++最常用的文档生成工具,本规范制定了标准化的Doxygen注释规则,要求对文件、类/结构体、变量/常量、函数进行全面注释,实现注释即文档,核心注释标签为@brief、@param、@return、@file等。
5.1 注释基础格式
使用C++风格的多行注释,以///开头(单行Doxygen注释),或/** ... */(多行Doxygen注释),禁止使用C风格的/* ... */普通注释。
5.2 文件注释(放在文件最开头)
分项目注释和文件注释两层,项目注释仅在项目入口文件中添加,所有文件均需添加文件注释。
项目注释示例
///////////////////////////////////////////////////////////////////////////
/// @mainpage 项目名称:简易电话簿系统
/// @author 作者姓名
/// @version 版本号:V1.0
/// @date 开发日期:2025年01月01日
/// @brief 项目简介:基于C++实现的简易电话簿系统,支持增删查显等功能
///////////////////////////////////////////////////////////////////////////
文件注释示例
///////////////////////////////////////////////////////////////////////////
/// @file data_manager.h
/// @brief 数据管理模块头文件
/// @details 封装了数据的读取、保存、解析等功能,为上层业务提供数据支撑
/// @author 作者姓名
/// @date 2025年01月01日
///////////////////////////////////////////////////////////////////////////
5.3 类/结构体注释(放在类/结构体定义上方)
- 核心标签:
@brief(简单概述)、@details(详细概述); - 注释需明确类/结构体的功能、设计思路、使用场景;
- 示例:
/// @brief 用户信息类 /// @details 封装了用户的基本信息(姓名、电话、ID),提供信息的存取接口, /// 是整个系统的核心数据结构,所有业务模块均依赖此类 class UserInfo { // 类内成员 };
5.4 常量/变量注释
分行前注释和行后注释两种方式,根据变量数量和命名长度选择:
- 行前注释:单个变量/少量变量,注释放在变量定义上方,用
///开头; - 行后注释:多个变量/命名较短的变量,注释放在变量定义右侧,用
///<开头;
- 示例:
// 行前注释 /// 系统最大用户数 #define MAX_USER_NUM 2048 /// 类的成员变量:用户ID int user_id_; // 行后注释 int a; ///< 临时变量a int b; ///< 临时变量b std::string name; ///< 用户名
5.5 函数注释(放在函数声明上方,头文件中)
函数注释是Doxygen注释的核心,必须包含功能概述、参数说明、返回值说明,核心标签为@brief、@param、@return,若函数无返回值则省略@return。
核心规则
@brief:简要描述函数的功能/行为,一句话概括;@param:描述每个参数的含义、取值范围、输入/输出属性,格式为@param 形参名 参数说明;@return:描述返回值的含义、取值范围、错误码标识;- 若函数抛出异常,需添加
@exception标签,描述异常类型和触发条件。
函数注释示例
/// @brief 添加用户信息到系统
/// @param user 待添加的用户信息对象,输入参数
/// @param err_code 错误码,输出参数:0表示成功,-1表示用户已存在,-2表示系统满
/// @return 成功返回true,失败返回false
bool AddUser(const UserInfo& user, int& err_code);
/// @brief 获取用户的电话信息
/// @param user_id 用户ID,输入参数,必须大于0
/// @return 成功返回用户电话字符串,失败返回空字符串
std::string GetUserPhone(int user_id);
5.6 Doxygen工具配置要求
- 项目设置:明确设置项目名称和项目语言(C/C++);
- 编码方式:
- VS开发的项目:设置
INPUT_ENCODING = GBK; - 跨平台项目:设置
INPUT_ENCODING = UTF-8(推荐);
- VS开发的项目:设置
- 文件匹配:设置
FILE_PATTERNS为*.h *.cpp *.c,匹配所有C/C++源文件和头文件; - 递归扫描:开启
RECURSIVE = YES,递归扫描项目目录下的所有文件。
六、规范核心总结与落地建议
核心总结
这份规范的核心是**「标准化、工程化、高可读」,所有规则均围绕团队协作和长期维护**设计,摒弃个人编码习惯,强调统一化:
- 头文件:防重包含、减依赖、禁命名空间,降低编译耦合;
- 类/结构体:清晰分工、少继承、避多重继承,降低设计复杂度;
- 格式:4空格缩进、Allman风格括号,统一视觉呈现;
- 命名:分类型制定规则,见名知意,无歧义;
- 注释:Doxygen标准化,注释即文档,提升可维护性。
团队落地建议
- 强制执行:将规范纳入团队开发手册,作为代码评审的必查项;
- 编辑器配置:统一配置编辑器(VS/Clion/Vim),实现自动缩进、空格替换Tab、命名提醒;
- 工具辅助:使用Lint工具(如Cppcheck、Clang-Tidy)检查代码格式和命名规范,自动发现违规;
- 文档生成:将Doxygen文档生成纳入项目构建流程,每次编译自动更新文档;
- 新人培训:将本规范作为团队新人的核心培训内容,确保所有成员理解并遵守。
这份规范不仅适用于中小型企业的C/C++开发团队,也可作为个人开发者的工程化编码参考,养成良好的编码习惯,提升代码质量和开发效率。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/weixin_42388661/article/details/158428734



