C++ 代码规范

本规范旨在提升团队协作效率与代码质量,通过统一编码风格,确保代码的可读性、可维护性与可扩展性。规范的制定有助于减少沟通成本、规避潜在错误,并为代码的长期演进奠定坚实基础,保障项目高效交付与稳定运行。

命名规范(匈牙利前缀 + 小驼峰命名法)

统一总规则:所有变量、函数统一使用小驼峰命名法,变量统一添加类型前缀。

1. 函数参数及普通变量命名

参数命名严格遵循「类型前缀 + 语义化名称」,命名需简洁明确、语义清晰、贴合用途,禁止无意义缩写,固定类型前缀如下:

  • double:前缀 d(示例:dOffsetdRadius
  • vector:前缀 vec(示例:vecStudentvecPersons
  • bool:前缀 b(示例:bIsValidbEnable
  • point:前缀 pt(示例:ptStartptEnd
  • int:前缀 n(示例:nCountnIndex
  • float:前缀f(示例:fScalefAngle
  • char:前缀 c(示例:cFlagcSeparator
  • string:前缀 str(示例:strNamestrPath
  • 枚举类型(enum):前缀 e(示例:eModeeStatus
  • 指针类型:前缀 p(示例:pPersonpBuffer

2. 全局变量命名

支持两种规范,二选一即可:

  1. 在变量命名规则基础上,添加g_前缀。示例:g_dGlobalScaleg_vecGlobalPersons
  2. 全大写字母,单词之间用下划线分隔。示例:GLOBAL_SCALEGLOBAL_FEATURES

3. 常量命名

支持两种规范,二选一即可:

  1. 在变量命名规则基础上,添加c_前缀。示例:c_dMaxOffsetc_nMinCount
  2. 全大写字母,单词之间用下划线分隔。示例:MAX_OFFSETMIN_COUNT

4. 函数命名

统一使用小驼峰命名法,首字母小写,后续每个单词首字母大写,遵循「动词 + 名词」结构,语义明确无歧义,示例:processStudentcalculateOffsetcheckValidity


类与结构体专属规范

本小节整合所有类、结构体、成员权限、成员变量相关全部规则,统一管理、便于查阅执行。

1. 类/结构体命名

采用大驼峰命名法,首字母大写,名称直接体现核心用途,简洁无歧义,示例:CircleStudentInfoPoint2D

2. 类成员权限管控

  • 成员变量必须为私有权限(private),如需外部访问、修改,必须提供对应的get/set方法,严禁外部直接修改私有成员变量
  • 不同权限的函数和成员,必须单独划分独立代码块,禁止不同权限内容混合穿插(private、protected、public代码块严格分开书写,不可交叉);
  • 所有私有成员变量,必须集中放在类声明的最后位置,单独一个private代码块统一声明,与成员函数完全分离。
  • 子类实现父类虚函数时,需要同时添加virtual和override关键字,确保虚函数继承关系清晰,避免隐式覆盖导致的逻辑异常。
  • 类的公有方法(public权限)必须编写注释,注释需清晰说明方法功能、参数含义、返回值类型及使用注意事项,提升代码可维护性和可读性。

正确示例

// 父类声明
class Base
{
public:
    // 公有虚函数,必须添加注释说明功能
    /**
         * @brief 处理人数据,核心业务入口
         * @param dParam 处理参数,范围:0.0~100.0
         * @return bool 处理成功返回true,失败返回false
         */
    virtual bool processData(double dParam) = 0;
};

// 子类实现,同时添加virtual和override关键字
class Student : public Base
{
public:
    // 公有方法必须添加注释,与父类虚函数注释呼应
    /**
     * @brief 重写父类方法,处理学生人数据
     * @param dParam 学生处理偏移参数,范围:0.0~100.0
     * @return bool 处理成功返回true,参数非法返回false
     */
    virtual bool processData(double dParam) override
    {
        // 业务逻辑实现
        return true;
    }

private:
    void initData();

private:
    double m_dOffset;
    vector<person*> m_vecStudent;
    bool m_bIsValid;
};
class Base2
{
public:
    // public权限的get/set方法,单独一个代码块,专供外部访问/修改成员变量
    double getOffset() const;
    void setOffset(double dOffset);

private:
    // private权限内部工具函数,单独一个代码块,与其他权限完全区分
    void initData();

private:
    // 成员变量统一为private权限,单独代码块,置于类声明最后
    double m_dOffset;
    vector<person*> m_vecStudent;
    bool m_bIsValid;
};

错误示例

// 父类声明
class Base
{
public:
    // 错误:公有虚函数未添加注释
    virtual bool processData(double dParam) = 0;
};

// 子类实现,存在两处错误
class Student : public Base
{
public:
    // 错误1:公有方法未添加注释;错误2:未同时添加virtual和override关键字
    bool processData(double dParam) // 仅写override或仅写virtual,均不符合规范
    {
        // 业务逻辑实现
        return true;
    }

private:
    // 错误:私有方法和成员变量穿插混合,位于同一个private块内
    void initData();
    double m_dOffset;
    vector<person*> m_vecStudent;
};
class Base2
{
public:
    double getOffset() const;
    void setOffset(double dOffset);
    // 错误:成员变量设为public,允许外部直接修改
    bool m_bIsValid;

private:
    // 错误:私有方法和成员变量穿插混合,位于同一个private块内
    void initData();
    double m_dOffset;
    vector<person*> m_vecStudent;
};

3. 类成员变量命名

在普通变量「类型前缀+语义化名称」的基础上,额外添加固定前缀m_,严格区分普通变量与类成员变量,示例:m_dOffsetm_vecStudentm_bIsValidm_ptCenter


通用格式规范

1. 声明与定义分离

  • 函数声明与定义必须强制分离,函数声明置于对应的.h头文件中,函数实现定义置于同名.cpp文件中;
  • 头文件首行必须添加头文件守卫【#pragma once】;
  • 头文件中严禁使用【using namespace】,彻底避免命名空间污染。

示例

// 头文件 XXX.h(仅存放函数声明)
#pragma once // 强制头文件守卫
void initData();
// 同名源文件 XXX.cpp(仅存放函数实现定义)
#include "XXX.h"
void initData()
{
    // 业务逻辑实现
}

2. 花括号书写

  • 函数定义、forwhileifelseswitchclassstruct 等所有语句,左右花括号必须单独占一行,与对应语句对齐;
  • if、for、while等条件/循环语句,即便内部只有1行逻辑,也必须加花括号包裹,严禁省略花括号;
  • 花括号规则不适用于变量初始化列表,初始化列表可正常同行书写;
  • 严禁左花括号紧跟语句末尾、同行书写。

正确示例

// 花括号单独成行,符合规范
vector<person*> processStudent()
{
    if (bIsValid)
    {
        return {}; // 单行逻辑,强制加花括号
    }

    // 初始化列表不受花括号规则约束
    vector<int> vecNums = {1, 2, 3, 4};
    return {};
}

错误示例

// 错误:左花括号与语句同行
vector<person*> processStudent() {
    if (bIsValid) {
        return {};
    }

    // 错误:单行逻辑省略花括号
    for (int i = 0; i < 5; i++)
        cout << i << endl;

    // 错误:单行逻辑省略花括号
    while (bIsRunning)
        updateStatus();
}

3. 空行使用

  • 函数内不同逻辑块之间,添加适量空行区分模块(类似写作文区分自然段),提升代码可读性;
  • 函数与函数之间保留1个空行,类内成员函数的声明之间、定义之间保留1个空行;
  • 禁止连续多个空行,严禁代码首尾多余空行。

4. 指针使用

  • 所有指针在使用前必须执行判空操作,杜绝空指针访问导致程序崩溃;
    指针判空推荐极简写法:
    • if (pPerson):判断指针非空,可理解为「如果指针存在」
    • if (!pPerson):判断指针为空,可理解为「如果指针不存在」

推荐写法

// 推荐极简判空写法
Person* pPerson = XXX();
if (pPerson) // 先判非空,再安全使用
{
    pPerson->XXX();
}
if (!pPerson) // 判空后按需输出日志
{
    // LOG(WARNING) << "pPerson is null";
}

不推荐/错误写法

// 不推荐:冗余冗余判空写法,不符合规范
Person* pPerson = XXX();
if (pPerson != nullptr) // 冗余多余
{
    pPerson->XXX();
}
if (pPerson == NULL) // 写法不统一、冗余
{
    // ...
}
// 严重错误:未判空直接使用指针
pPerson->XXX();

判空同时需要输出日志、跳过当前逻辑的,可使用项目统一封装的断言宏,简化代码:

for (person* pPerson : vecPersons)
{
    LOG_WARN_CONTINUE_UNLESS(pPerson,"pPerson is null");
}

// 上述代码等价于完整判空+日志+跳过逻辑,代码更简洁
for (person* pPerson : vecPersons)
{
    if (!pPerson)
    {
        LOG(WARNING) << "pPerson is null";
        continue;
    }
}
  • 严禁指针链式调用,必须逐级拆解、逐级判空,避免链式空指针崩溃。

推荐逐级判空写法

void func(person_factory* pFactory)
{
    if (!pFactory)
    {
        return;
    }
    archer* pArcher = pFactory->Archer();

    if (!pArcher)
    {
        return;
    }
    Teacher* pTeacher = pArcher->m_pteacher;
}

禁止的链式调用写法

void func(person_factory* pFactory)
{
    // 严重错误:链式调用,任意一级为空都会直接崩溃
    Teacher* pTeacher = pFactory->Archer()->m_pteacher;
}

5. 缩进

嵌套代码逐层缩进,层级对齐清晰,禁止乱缩进、内层代码不缩进,保证代码结构一目了然。

6. 空格

  • 算术运算符(+、-、、/、%)、比较运算符(==、!=、<、>、<=、>=)、赋值运算符(=、+=、-=、=、/=)两侧,各保留1个空格;
  • 函数参数列表逗号后加1个空格,逗号前无空格;
  • if/for/while 关键字与括号之间加1个空格,括号内部首尾无空格;
  • 函数名与括号之间严禁加空格。

7. 注释

  • 单行注释统一使用 //,注释内容与// 之间保留1个空格,仅用于标注关键逻辑、特殊处理、易错坑点;
  • 严禁无效注释、冗余注释、注释与代码逻辑不符。

8. 代码质量与工程通用

变量定义规则

变量在使用时就近定义,严禁统一集中在函数/代码块开头定义,符合 Effective C++ 规范,避免无效资源占用。

嵌套层级控制

严格控制代码嵌套层级,优先使用反判断提前返回/跳过,扁平化代码逻辑,大幅提升可读性与可维护性,禁止超过3层的深层嵌套。

函数长度限制

单个函数代码行数严格控制在200行以内,复杂、重复的逻辑必须提取为独立公共函数,严禁超大函数、单函数承载多段无关业务。

逻辑复用

公共业务逻辑、通用工具逻辑必须封装为独立函数,严禁复制粘贴代码后仅修改局部内容,保证同一段逻辑只需要修改一处即可全量生效。

其他强制规则

  • 严禁使用无意义魔法数字,固定数值统一定义为常量,常量命名优先使用全大写、单词下划线分隔格式;
  • 超长代码行在运算符前换行,换行后与上一行对齐缩进;
  • switch 语句每个case独立缩进,每个case逻辑必须加花括号包裹,break紧跟逻辑结尾,多case共用逻辑必须添加注释说明。

9. 匿名空间使用规则

仅在当前文件使用的局部函数,统一放入当前cpp文件的匿名命名空间,严禁在全局作用域直接书写,避免全局命名冲突、重定义错误,提升代码封装性。

10. 全局配置信息

多文件复用的固定属性名、常量配置,统一在独立头文件中集中定义,使用constexpr 修饰,禁止在多个文件中复制硬编码字符串、数值,便于统一维护、一键修改。

11. auto关键字使用

简单基础类型禁止使用auto关键字,auto仅可在以下场景使用,保证代码类型可读性、无歧义:

  • 定义lambda表达式时(必须使用auto);
  • 类型名称过长、书写繁琐时,如STL容器迭代器、复杂模板类型。

12. 数学计算(后端)

  • 凡是分母为变量的,必须考虑分母为0的情况。
  • 浮点数之间的比较应使用 MathUtils 下的方法

13. 线程创建(后端)

  • 创建线程应使用线程池 ThreadPool 统一管理

代码提交信息规范

禁止空提交信息,所有提交必须标注清晰、可追溯。

1. bug修复提交

严格按照以下格式填写,保证bug可追溯、根因与方案对应:

[bug 编号] bug标题
根本原因:XXX(精准说明bug核心成因,禁止模糊描述,如“代码错误”“逻辑异常”)
解决方法:XXX(详细说明修复方案、修改模块、核心逻辑,与根因完全对应)

自行发现、无bug编号的问题,编号项可省略。

2. 功能相关提交

功能开发、优化、调整等提交,按照以下格式填写,明确修改内容:

[功能名] 核心修改内容(简洁明确)
补充说明:修改细节、修改原因、新增/优化的核心逻辑(可选)

3. 其他通用修改

非bug、非特定功能的通用修改,直接清晰填写修改内容即可。

posted @ 2026-06-22 17:36  小松鼠树懒  阅读(25)  评论(0)    收藏  举报