编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • 质量保障

  • 产品思考

  • 软实力

  • 开发流程

  • Git应用

  • 技术模版

  • 技术规范

    • 技术规范
    • C++编程代码规范指南
      • 目录
      • 01.规范概述
        • 1.1 为何需要代码规范
        • 1.2 核心目标
        • 1.3 要求等级
        • 1.4 C++版本
      • 02.头文件规范
        • 2.1 Self-contained 头文件 【必须】
        • 2.2 头文件保护 【必须】
        • 2.3 避免前置声明 【推荐】
        • 2.4 内联函数 【推荐】
        • 2.5 #include 顺序 【必须】
      • 03.命名规范
        • 3.1 命名总表
        • 3.2 正确与错误示例
        • 3.3 命名反模式
      • 04.代码格式规范
        • 4.1 缩进与空格 【必须】
        • 4.2 大括号风格 【必须】
        • 4.3 换行与对齐
        • 4.4 空行与分隔
      • 05.注释规范
        • 5.1 核心原则
        • 5.2 类注释
        • 5.3 函数注释
        • 5.4 TODO 与 FIXME
      • 06.类设计规范
        • 6.1 构造与析构 【必须】
        • 6.2 隐式类型转换 【必须】
        • 6.3 拷贝和移动 【必须】
        • 6.4 继承 【必须】
        • 6.5 成员变量 【必须】
        • 6.6 声明顺序 【必须】
        • 6.7 运算符重载 【必须】
      • 07.函数规范
        • 7.1 输入与输出 【推荐】
        • 7.2 编写短函数 【推荐】
        • 7.3 函数重载 【必须】
        • 7.4 缺省参数 【推荐】
        • 7.5 Lambda 表达式 【推荐】
      • 08.现代C++特性
        • 8.1 智能指针 【必须】
        • 8.2 右值引用与移动语义 【推荐】
        • 8.3 const 与 constexpr 【必须】
        • 8.4 类型转换 【必须】
        • 8.5 auto 关键字 【推荐】
        • 8.6 前置自增 【必须】
        • 8.7 整型 【推荐】
        • 8.8 结构化绑定 【推荐】
        • 8.9 std::optional 与 std::variant 【推荐】
        • 8.10 if 与 switch 初始化语句 【推荐】
        • 8.11 枚举与枚举类 【必须】
      • 09.异常与错误处理
        • 9.1 异常使用策略
        • 9.2 RAII 原则 【必须】
        • 9.3 错误码与返回值 【推荐】
        • 9.4 noexcept 规范 【推荐】
        • 9.5 断言与防御 【推荐】
      • 10.并发与性能
        • 10.1 线程安全 【必须】
        • 10.2 锁选型指南 【推荐】
        • 10.3 原子操作 【推荐】
        • 10.4 线程局部存储 【必须】
        • 10.5 性能优化
        • 10.6 缓存友好的数据结构 【推荐】
      • 11.工具链与自动化
        • 11.1 静态代码检查
        • 11.2 clang-format 配置
        • 11.3 CI 集成
        • 11.4 编译期防御 【推荐】
        • 11.5 pre-commit 钩子 【推荐】
      • 12.代码审查清单
      • 13.常见陷阱速查
        • 13.1 生命周期陷阱
        • 13.2 类型陷阱
        • 13.3 性能陷阱
    • Java编程代码规范指南
    • JavaScript编程规范指南
    • TypeScript编程规范指南
    • Python编程代码规范指南
    • Go编程代码规范指南
    • Kotlin编程代码规范指南
    • Swift编程代码规范指南
    • Rust编程代码规范指南
    • Shell编程代码规范指南
    • 项目代码提交规范
  • markdown

  • mermaid

  • license

  • 博客部署

  • 技术招聘

  • 测试经验

  • 技术
  • 技术规范
杨充
2020-04-10
目录

C++编程代码规范指南

# C++编程代码规范指南

本规范参考 Google C++ Style Guide (opens new window),结合项目实践精简整理。

# 目录

  • 01.规范概述
    • 1.1 为何需要代码规范
    • 1.2 核心目标
    • 1.3 要求等级
    • 1.4 C++版本
  • 02.头文件规范
    • 2.1 Self-contained头文件
    • 2.2 头文件保护
    • 2.3 避免前置声明
    • 2.4 内联函数
    • 2.5 include顺序
  • 03.命名规范
    • 3.1 命名总表
    • 3.2 正确与错误示例
    • 3.3 命名反模式
  • 04.代码格式规范
    • 4.1 缩进与空格
    • 4.2 大括号风格
    • 4.3 换行与对齐
    • 4.4 空行与分隔
  • 05.注释规范
    • 5.1 核心原则
    • 5.2 类注释
    • 5.3 函数注释
    • 5.4 TODO与FIXME
  • 06.类设计规范
    • 6.1 构造与析构
    • 6.2 隐式类型转换
    • 6.3 拷贝和移动
    • 6.4 继承
    • 6.5 成员变量
    • 6.6 声明顺序
    • 6.7 运算符重载
  • 07.函数规范
    • 7.1 输入与输出
    • 7.2 编写短函数
    • 7.3 函数重载
    • 7.4 缺省参数
    • 7.5 Lambda表达式
  • 08.现代C++特性
    • 8.1 智能指针
    • 8.2 右值引用与移动语义
    • 8.3 const与constexpr
    • 8.4 类型转换
    • 8.5 auto关键字
    • 8.6 前置自增
    • 8.7 整型
    • 8.8 结构化绑定
    • 8.9 std-optional-与-std-variant
    • 8.10 if-与-switch-初始化语句
    • 8.11 枚举与枚举类
  • 09.异常与错误处理
    • 9.1 异常使用策略
    • 9.2 RAII原则
    • 9.3 错误码与返回值
    • 9.4 noexcept规范
    • 9.5 断言与防御
  • 10.并发与性能
    • 10.1 线程安全
    • 10.2 锁选型指南
    • 10.3 原子操作
    • 10.4 线程局部存储
    • 10.5 性能优化
    • 10.6 缓存友好的数据结构
  • 11.工具链与自动化
    • 11.1 静态代码检查
    • 11.2 clang-format配置
    • 11.3 CI集成
    • 11.4 编译期防御
    • 11.5 pre-commit-钩子
  • 12.代码审查清单
  • 13.常见陷阱速查
    • 13.1 生命周期陷阱
    • 13.2 类型陷阱
    • 13.3 性能陷阱

# 01.规范概述

# 1.1 为何需要代码规范

疑惑:代码能跑就行,为什么还要花时间制定规范?

答疑:C++ 是一门强大的语言,但也是公认的"最难写对的语言"。同一个功能有 10 种写法,其中 7 种有隐蔽的未定义行为。规范的本质是用统一的方式写安全的代码。

# 1.2 核心目标

目标 说明
可读性 代码像散文一样流畅
一致性 整个项目像一个人写的
安全性 从编码层面避免内存错误、未定义行为
可维护性 半年后自己还能看懂
可审查性 Code Review 聚焦逻辑而非格式

# 1.3 要求等级

  • 必须(Mandatory):必须采用
  • 推荐(Preferable):理应采用,特殊情况可不采用
  • 可选(Optional):自行决定

# 1.4 C++版本

代码应针对 C++17,不使用 C++20 特性。不要使用非标准扩展。


# 02.头文件规范

# 2.1 Self-contained 头文件 【必须】

头文件应该自给自足,以 .h 结尾。禁止使用 -inl.h 头文件。

// ✅ 正确:头文件包含了所需的所有依赖
#pragma once

#include <string>
#include <vector>

class UserManager {
public:
    std::string getName(int id);
    std::vector<int> getIds();
};

// ❌ 错误:依赖使用方先包含了 <string>
#pragma once
class UserManager {
public:
    std::string getName(int id);  // 编译可能失败!
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 2.2 头文件保护 【必须】

所有头文件使用 #pragma once 或 #define 防止多重包含。

// ✅ 方式一:推荐
#pragma once

// ✅ 方式二:传统宏保护
#ifndef MYPROJECT_FOO_BAR_H_
#define MYPROJECT_FOO_BAR_H_
// ...
#endif  // MYPROJECT_FOO_BAR_H_
1
2
3
4
5
6
7
8

# 2.3 避免前置声明 【推荐】

优先使用 #include,尽量避免前置声明。

// ✅ 直接包含
#include "user.h"

// ❌ 尽量避免:前置声明隐藏依赖,可能被后续改动破坏
class User;
1
2
3
4
5

# 2.4 内联函数 【推荐】

只有 10 行以内的函数才声明为内联。

// ✅ 短小的存取函数适合内联
inline int getId() const { return id_; }

// ❌ 长函数不要内联
inline void processLargeDataset() {
    for (int i = 0; i < 10000; ++i) {
        // 大量逻辑...
    }
}
1
2
3
4
5
6
7
8
9

# 2.5 #include 顺序 【必须】

// 1. 对应的头文件(优先位置)
#include "foo/public/fooserver.h"

// 2. C 系统头文件
#include <sys/types.h>
#include <unistd.h>

// 3. C++ 标准库
#include <string>
#include <vector>

// 4. 其他库的头文件
#include "base/basictypes.h"

// 5. 本项目头文件
#include "foo/bar.h"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

每组之间用空行分隔。


# 03.命名规范

# 3.1 命名总表

类型 规则 示例
文件名 全小写 + 下划线 user_manager.h, thread_pool.cc
类名 大驼峰 UserManager, ThreadPool
函数名 大驼峰 GetUserName(), ProcessData()
变量名 全小写 + 下划线 user_name, thread_count
成员变量 小写 + 下划线 + 后缀 _ user_name_, count_
常量 k 开头 + 大驼峰 kMaxRetryCount, kDefaultPort
宏名 全大写 + 下划线 MAX_BUFFER_SIZE
命名空间 全小写 + 下划线 my_project::foo_bar
枚举 同常量或全大写 kOk, kError 或 COLOR_RED

# 3.2 正确与错误示例

// ✅ 正确
class UserManager {
public:
    std::string GetUserName(int user_id) const;
private:
    std::string user_name_;      // 成员变量带下划线后缀
    int login_count_;
    static constexpr int kMaxRetryCount = 3;
};

// ❌ 错误
class usermanager {              // 类名未大驼峰
public:
    std::string get_user_name(int user_id);  // 函数名未大驼峰
    std::string userName;        // 成员变量未用下划线后缀
    static const int MAX_RETRY = 3;  // 常量未用 k 前缀
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 3.3 命名反模式

反模式 示例 改进
过度缩写 usr_mgr user_manager
含义不清 data, info, tmp user_data, config_info
否定命名 is_not_empty is_empty() 反转
拼音命名 dian_hua phone_number

# 04.代码格式规范

# 4.1 缩进与空格 【必须】

// ✅ 使用 2 个空格缩进(Google 风格)
int main() {
  for (int i = 0; i < 10; ++i) {
    if (i % 2 == 0) {
      std::cout << i << std::endl;
    }
  }
  return 0;
}

// ✅ 运算符两侧加空格
int result = a + b * c;
bool ok = (x > 0) && (y < 10);

// ✅ 逗号后加空格
void Init(const std::string& name, int age, bool active);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 4.2 大括号风格 【必须】

// ✅ K&R 风格:左大括号不换行
class UserService {
 public:
  void Process() {
    if (condition) {
      DoSomething();
    } else {
      DoOther();
    }
  }
};

// ❌ 左大括号换行(不推荐)
class UserService
{
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 4.3 换行与对齐

// ✅ 参数过长时换行对齐
void SendNotification(
    const std::string& user_id,
    const std::string& title,
    const std::string& content,
    NotificationType type);

// ✅ 条件表达式过长时换行
if (user_status == Status::ACTIVE
    && user_role == Role::ADMIN
    && HasPermission(Permission::WRITE)) {
  // ...
}

// 单行长度不超过 120 字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 4.4 空行与分隔

class UserService {
 public:
  // 构造与成员变量之间空一行
  UserService(UserRepo* repo) : repo_(repo) {}

  // 方法之间空一行
  User GetUser(int id) {
    User cached = cache_.Get(id);
    if (cached.valid()) {
      return cached;            // 卫语句提前返回
    }

    User user = repo_->FindById(id);  // 不同逻辑块之间空一行
    cache_.Put(id, user);
    return user;
  }

  void DeleteUser(int id);

 private:
  UserRepo* repo_;
  Cache cache_;
  static constexpr int kMaxRetry = 3;  // 不同逻辑组之间空一行
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 05.注释规范

# 5.1 核心原则

注释解释"为什么",代码说明"做了什么"。

// ❌ 差的注释:复述代码
i++;  // i 加 1

// ✅ 好的注释:解释意图
i++;  // 跳过 CSV 文件的标题行

// ❌ 过时注释(危险!)
// 调用 V1 接口(注:已改为 V2)
ProcessV2();

// ✅ 记录决策原因
// 使用重试机制,因为第三方接口在高峰期偶尔超时(2020-03 确认的问题)
RetryWithBackoff(kMaxRetry);
1
2
3
4
5
6
7
8
9
10
11
12
13

# 5.2 类注释

// 用户管理服务,负责用户的 CRUD 和权限管理。
//
// 线程安全性:该类是线程安全的,内部用 std::mutex 保护共享数据。
// 使用示例:
//   UserService svc(repo, cache);
//   auto user = svc.GetUser(123);
class UserService {
 public:
  // ...
};
1
2
3
4
5
6
7
8
9
10

# 5.3 函数注释

// 根据 ID 查询用户信息。
//
// 优先从缓存获取,未命中时查数据库。查询结果自动写入缓存。
//
// 参数:
//   user_id - 用户唯一标识,必须 > 0
// 返回:
//   用户对象;如果用户不存在,返回 std::nullopt
std::optional<User> GetUser(int user_id);
1
2
3
4
5
6
7
8
9

# 5.4 TODO 与 FIXME

// TODO(yc): 添加分页查询支持,预计 2.0 版本实现
std::vector<User> GetAllUsers();

// FIXME(yc): 当 user_id 为 INT_MAX 时溢出,需要增加边界检查
int64_t CalculateHash(int user_id);
1
2
3
4
5

# 06.类设计规范

# 6.1 构造与析构 【必须】

// ✅ 不要在构造函数中调用虚函数
class Base {
 public:
  Base() { Init(); }          // ❌ Init() 调用虚函数——派生的实现不会被调用
  virtual void Init() = 0;
};

// ✅ 用工厂函数替代复杂的构造
class UserManager {
 public:
  static std::unique_ptr<UserManager> Create(const Config& config);
 private:
  UserManager() = default;  // 构造私有,通过工厂创建
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 6.2 隐式类型转换 【必须】

// ✅ 单参数构造函数加 explicit
class StringWrapper {
 public:
  explicit StringWrapper(const std::string& s);  // ✅
  // StringWrapper(const std::string& s);         // ❌ 可能发生隐式转换
};
1
2
3
4
5
6

# 6.3 拷贝和移动 【必须】

// ✅ 需要时支持;否则显式禁用
class NonCopyable {
 public:
  NonCopyable() = default;
  NonCopyable(const NonCopyable&) = delete;
  NonCopyable& operator=(const NonCopyable&) = delete;
};

// ✅ 移动构造函数声明为 noexcept
class Buffer {
 public:
  Buffer(Buffer&& other) noexcept;  // noexcept 使 std::vector 可移动
};
1
2
3
4
5
6
7
8
9
10
11
12
13

# 6.4 继承 【必须】

// ✅ 优先使用组合,只在"IS-A"关系时使用继承
// 不推荐:继承导致强耦合
class OrderService : public BaseService { };

// 推荐:组合更灵活
class OrderService {
 public:
  explicit OrderService(BaseService* base) : base_(base) {}
 private:
  BaseService* base_;
};

// ✅ 继承时:public 继承 + override 关键字
class Dog : public Animal {
 public:
  void Speak() override;  // ✅ 明确标记覆盖
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 6.5 成员变量 【必须】

// ✅ 数据成员全部为 private(static const 除外)
class UserInfo {
 public:
  int id() const { return id_; }    // accessor
  void set_name(const std::string& s) { name_ = s; }
 private:
  int id_;
  std::string name_;
  static constexpr int kMaxAge = 150;
};
1
2
3
4
5
6
7
8
9
10

# 6.6 声明顺序 【必须】

class MyClass {
 public:          // ① public 段在最前
  // 类型别名
  using IdType = int64_t;
  // 常量
  static constexpr int kDefaultPort = 8080;
  // 构造/析构
  MyClass();
  ~MyClass();
  // 非拷贝/移动(按需)
  // 方法
  void DoSomething();

 protected:       // ② protected
  void HelperMethod();

 private:         // ③ private 在最后
  int value_;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 6.7 运算符重载 【必须】

// ✅ 只在语义明确时重载
class Complex {
 public:
  Complex operator+(const Complex& other) const;  // ✅ 数学语义
  bool operator==(const Complex& other) const;    // ✅ 比较语义

  // ❌ 不要重载 && || , 一元 &
};

// ✅ 定义了 < 就应该定义所有比较运算符
bool operator<(const Foo& a, const Foo& b);
bool operator>(const Foo& a, const Foo& b);
bool operator<=(const Foo& a, const Foo& b);
1
2
3
4
5
6
7
8
9
10
11
12
13

# 07.函数规范

# 7.1 输入与输出 【推荐】

// ✅ 优先用返回值作为输出
std::optional<std::string> TryParse(const std::string& input);

// 调用方
auto result = TryParse("42");
if (result && *result > 0) {
  // 使用 result
}

// ✅ C++17 结构化绑定返回多值
std::tuple<int, std::string> Process(const Request& req);
auto [status, message] = Process(req);

// 参数顺序:输入参数在前,输出参数在后
void FillResponse(const Request& req, Response* resp);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 7.2 编写短函数 【推荐】

  • 一个函数超过 40 行 → 考虑拆分
  • 一个函数做一件事
  • 善用卫语句减少嵌套
// ❌ 嵌套太深
void Process(Order* order) {
    if (order != nullptr) {
        if (order->IsValid()) {
            if (!order->IsProcessed()) {
                DoProcess(order);
            }
        }
    }
}

// ✅ 卫语句扁平化
void Process(Order* order) {
    if (order == nullptr) return;
    if (!order->IsValid()) return;
    if (order->IsProcessed()) return;
    DoProcess(order);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 7.3 函数重载 【必须】

// ✅ 重载版本之间语义无差异时使用
void Analyze(const std::string& text);
void Analyze(const char* text, size_t length);

// ❌ 仅通过 const 或参数类型细微差别区分 → 让调用者困惑
1
2
3
4
5

# 7.4 缺省参数 【推荐】

// ✅ 非虚函数中可用
void Connect(const std::string& host, int port = 8080);

// ❌ 虚函数中禁止使用(派生类的默认值可能不同)
1
2
3
4

# 7.5 Lambda 表达式 【推荐】

// ✅ 短 lambda:默认按引用捕获,写在一行
std::sort(v.begin(), v.end(), [](int a, int b) { return a > b; });

// ✅ 需要捕获外部变量时,优先显式列出
int threshold = 10;
auto it = std::find_if(v.begin(), v.end(),
                       [threshold](int x) { return x > threshold; });

// ✅ 泛型 lambda(C++14)
auto twice = [](auto x) { return x * 2; };

// ✅ 捕获 this 时,C++17 起用 *this 避免悬空
class Worker {
    void Schedule() {
        timer_.Async([*this] { Process(); });  // 拷贝整个对象
        // timer_.Async([this] { Process(); });  // ❌ this 可能已析构
    }
};

// ✅ 长 lambda(>5 行)考虑提取为具名函数
// ❌ 避免:lambda 内又嵌套 lambda(可读性灾难)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 08.现代C++特性

# 8.1 智能指针 【必须】

// ✅ std::unique_ptr:独占所有权
std::unique_ptr<User> CreateUser(const std::string& name);

// ✅ std::shared_ptr:共享所有权(仅在确实需要时使用)
std::shared_ptr<Config> GetGlobalConfig();

// ❌ 不要到处使用 shared_ptr(有引用计数开销)
// ❌ 不要手动 new/delete(用智能指针或容器管理)
1
2
3
4
5
6
7
8

# 8.2 右值引用与移动语义 【推荐】

// ✅ 定义移动构造函数(noexcept)
class Buffer {
 public:
  Buffer(Buffer&& other) noexcept : data_(other.data_), size_(other.size_) {
    other.data_ = nullptr;
    other.size_ = 0;
  }
 private:
  char* data_;
  size_t size_;
};

// ✅ std::move 转移所有权
auto buffer2 = std::move(buffer1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 8.3 const 与 constexpr 【必须】

// ✅ API 中合理使用 const
std::string GetName() const;                    // const 成员函数
void Process(const std::string& input);          // const 引用参数

// ✅ constexpr 定义编译时常量
constexpr int kMaxConnections = 100;
constexpr int Square(int x) { return x * x; }   // constexpr 函数
1
2
3
4
5
6
7

# 8.4 类型转换 【必须】

// ✅ 使用 C++ 风格的类型转换
int64_t y = int64_t{1} << 42;          // 括号初始化
double d = static_cast<double>(i);     // static_cast
const_cast<char*>(str);                // const_cast(谨慎)
reinterpret_cast<uintptr_t>(ptr);      // reinterpret_cast(极谨慎)

// ❌ 不要使用 C 风格转换
int y = (int)x;                        // 禁止
1
2
3
4
5
6
7
8

# 8.5 auto 关键字 【推荐】

// ✅ 迭代器、lambda 等类型冗长时使用
auto it = container.begin();
auto result = std::make_unique<ComplexType>(args...);

// ❌ 不要滥用:当显式类型让代码更清晰时,写出类型
int user_count = 0;                    // ✅ 比 auto 更清晰
auto user_count = 0;                   // ❌ 不清楚是 int 还是 size_t
1
2
3
4
5
6
7

# 8.6 前置自增 【必须】

// ✅ 对迭代器和模板类型使用 ++i
for (auto it = v.begin(); it != v.end(); ++it) { }

// ✅ 循环中对简单数值,两者都行
for (int i = 0; i < 10; ++i) { }
1
2
3
4
5

# 8.7 整型 【推荐】

// ✅ 普通整型使用 int
int count = 0;
for (int i = 0; i < 10; ++i) { }

// ✅ 需要确定大小时用 <cstdint> 的精确宽度类型
int64_t large_value = 0;
uint32_t flags = 0;

// ❌ 避免使用无符号整型(除非位运算或表示位组)
// 不要仅为了"不会为负"而用 unsigned
1
2
3
4
5
6
7
8
9
10

# 8.8 结构化绑定 【推荐】

// ✅ 返回多值:用结构化绑定接收
std::tuple<int, std::string> GetUserInfo();
auto [id, name] = GetUserInfo();           // C++17

// ✅ 遍历 map
std::map<int, std::string> m = {{1, "a"}, {2, "b"}};
for (const auto& [key, value] : m) {
    std::cout << key << " -> " << value << '\n';
}

// ✅ 解构 struct / pair
struct Point { int x, y; };
Point p{10, 20};
auto [px, py] = p;                         // px=10, py=20

// ❌ 滥用:当字段名本身已足够清晰时,不需要结构化绑定
int user_id = user.id();                    // ✅ 更清晰
auto [id, name] = user.GetTuple();          // ❌ id 是什么 id?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 8.9 std::optional 与 std::variant 【推荐】

// ✅ std::optional:表达"可能没有值"
std::optional<User> FindUser(int id) {
    if (auto it = users_.find(id); it != users_.end()) {
        return it->second;
    }
    return std::nullopt;
}

// 调用方:
auto user = FindUser(123);
if (user) { Process(*user); }                // ✅ 显式检查
// *user                // ❌ 未检查直接解引用——UB
// user.value()         // ❌ 抛 std::bad_optional_access
auto name = user.value_or(User{"unknown"});  // ✅ 安全默认值

// ✅ std::variant:类型安全的联合体
using Result = std::variant<User, Error, Timeout>;
Result r = FetchRemote(123);

std::visit([](auto&& arg) {
    using T = std::decay_t<decltype(arg)>;
    if constexpr (std::is_same_v<T, User>)    { Process(arg); }
    else if constexpr (std::is_same_v<T, Error>)  { LogError(arg); }
}, r);

// ❌ 过度的 variant 让调用方负担过重——超过 4 个备选考虑重构
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

# 8.10 if 与 switch 初始化语句 【推荐】

// ✅ if-init(C++17):把变量作用域限制在 if 块内
if (auto it = map.find(key); it != map.end()) {
    Process(it->second);                     // it 仅这里可见
}  // it 析构

// ✅ 等价于老写法,但更干净:
auto it = map.find(key);
if (it != map.end()) { Process(it->second); }
// it 仍然可见,作用域泄露

// ✅ switch-init(C++17)
switch (auto status = GetStatus(); status) {
case Status::OK:     return;
case Status::RETRY:  return Retry();
}

// ✅ lock + if-init:完美搭档
if (std::lock_guard g(mu_); cache_.contains(key)) {
    return cache_[key];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 8.11 枚举与枚举类 【必须】

// ✅ C++11 起一律用 enum class(强类型枚举)
enum class Color { Red, Green, Blue };
enum class Status { OK, Error, Timeout };

Color c = Color::Red;                    // ✅ 必须带作用域
// int x = Color::Red;                   // ❌ 不隐式转 int
int x = static_cast<int>(Color::Red);    // ✅ 显式转换

// ✅ 指定底层类型(二进制协议 / 节省空间)
enum class Perm : uint8_t { Read = 0x1, Write = 0x2, Exec = 0x4 };
static_assert(sizeof(Perm) == 1);

// ❌ 禁止 C 风格 enum
enum OldColor { RED, GREEN };            // ❌ 污染作用域,隐式转 int
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 09.异常与错误处理

# 9.1 异常使用策略

// ✅ 保持团队一致:要么全用异常,要么全不用
// 对于不用异常的项目:
//   - 用返回码或 std::optional 报告错误
//   - 不要引入抛出异常的代码

// ✅ 使用异常时:
//   - 按值抛出,按引用捕获
//   - 不要到处捕获异常,只捕获能处理的
//   - 确保异常安全:使用 RAII 管理资源
1
2
3
4
5
6
7
8
9

# 9.2 RAII 原则 【必须】

// ✅ RAII:资源获取即初始化
class FileGuard {
 public:
  explicit FileGuard(const char* path) : fp_(fopen(path, "r")) {}
  ~FileGuard() { if (fp_) fclose(fp_); }
  FILE* get() const { return fp_; }
 private:
  FILE* fp_;
};

// ❌ 手动管理资源
void BadExample() {
  FILE* fp = fopen("data.txt", "r");
  // ... 如果中间 return 或抛异常,fp 泄漏
  fclose(fp);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 9.3 错误码与返回值 【推荐】

// ✅ 用 std::optional 表示可能找不到
std::optional<User> FindUser(int id);

// ✅ 用 std::expected(C++23)或自定义 Result 表示可能失败
// C++23:
std::expected<User, Error> CreateUser(std::string_view name);

// C++17 替代:
struct Result { User user; Error error; bool ok; };
// C++17 之前:
std::pair<User, Error> CreateUser(std::string_view name);  // 不太好

// ✅ 布尔返回值带 [[nodiscard]](C++17)
[[nodiscard]] bool TryLock();
TryLock();                  // ⚠ 编译器警告:忽略了返回值

// ❌ 用输出参数报错(不透明)
bool CreateUser(const std::string& name, User* out, Error* err);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

错误处理分层:

┌─────────────────────────────────────────────┐
│ 层         │ 策略                           │
├────────────┼────────────────────────────────┤
│ 对外 API   │ 返回 optional / Result         │
│ 内部函数   │ assert + 返回码                 │
│ 不可能路径 │ assert / abort                  │
│ 构造函数   │ 抛异常(因为不能返回值)         │
└─────────────────────────────────────────────┘
1
2
3
4
5
6
7
8

# 9.4 noexcept 规范 【推荐】

// ✅ 移动构造/赋值加 noexcept(STL 容器依赖它)
class Buffer {
 public:
  Buffer(Buffer&& other) noexcept;               // ✅
  Buffer& operator=(Buffer&& other) noexcept;    // ✅
};

// ✅ 不抛异常的函数加 noexcept
int GetValue() const noexcept;                   // 纯返回成员
void Swap(Foo& a, Foo& b) noexcept;              // 交换操作

// ✅ 析构函数默认 noexcept,不要主动抛异常
~Resource() {
    // ❌ 析构里抛异常 → std::terminate
    // ✅ 如果必须处理,吞掉并 log
}

// ❌ 不保证 noexcept 的函数不要标记
// 因为 noexcept 等于一个运行时承诺:抛了就 abort
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 9.5 断言与防御 【推荐】

// ✅ 前置条件用 assert 检查
User* GetUser(int id) {
  assert(id > 0 && "user_id must be positive");
  return FindInCache(id);
}

// ✅ 防御性编程:对外接口检查参数
void ProcessRequest(const Request* req) {
  if (req == nullptr) {
    LOG(ERROR) << "null request";
    return;
  }
  // ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 10.并发与性能

# 10.1 线程安全 【必须】

// ✅ 明确标注线程安全性
// 线程安全的类
class ThreadSafeCache {
 public:
  void Put(const std::string& key, const std::string& value);
 private:
  mutable std::mutex mu_;
  std::unordered_map<std::string, std::string> data_ GUARDED_BY(mu_);
};

// ✅ 优先使用 std::mutex + std::lock_guard
void UpdateData() {
  std::lock_guard<std::mutex> lock(mu_);
  data_["key"] = "value";
}

// ✅ 加锁顺序一致,避免死锁
// 始终按相同顺序获取多个锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 10.2 锁选型指南 【推荐】

// 决策树:
//
//    单变量? ───yes──▶ std::atomic<T>
//    多变量? ───yes──▶
//       读写比 > 10:1 且临界区 > 1μs?
//         ───yes──▶ std::shared_mutex
//         ───no ──▶ std::mutex + lock_guard
//

// ✅ 90% 场景:std::mutex + std::lock_guard
class Counter {
 public:
  void Inc() { std::lock_guard g(mu_); ++n_; }
  int Snapshot() const { std::lock_guard g(mu_); return n_; }
 private:
  mutable std::mutex mu_;
  int n_ = 0;
};

// ✅ 同时锁多把:std::scoped_lock(C++17,防死锁)
void Transfer(Account& a, Account& b, long n) {
    std::scoped_lock g(a.mu_, b.mu_);    // 原子锁两把
    a.balance_ -= n; b.balance_ += n;
}

// ✅ 读写锁:临界区大 且 读 >> 写
std::shared_mutex rw_;
std::string Read() const {
    std::shared_lock g(rw_);             // 共享读
    return data_;
}
void Write(std::string s) {
    std::unique_lock g(rw_);             // 排他写
    data_ = std::move(s);
}

// ❌ 反模式:手动 lock/unlock
mu_.lock();
// ... 可能抛异常
mu_.unlock();                            // 可能永远走不到
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
33
34
35
36
37
38
39
40

# 10.3 原子操作 【推荐】

// ✅ 计数器用 std::atomic(无锁)
std::atomic<size_t> req_count_{0};
void OnRequest() { req_count_.fetch_add(1, std::memory_order_relaxed); }

// ✅ 状态标志用 atomic<bool>
std::atomic<bool> stop_{false};
void Worker() {
    while (!stop_.load(std::memory_order_acquire)) {
        DoWork();
    }
}

// ✅ 发布大对象:release/acquire 配对
std::string data_;                       // 数据
std::atomic<bool> ready_{false};         // 标志

// 发布端
void Publish(std::string s) {
    data_ = std::move(s);                // ① 先写数据
    ready_.store(true, std::memory_order_release);  // ② 再设标志
}
// 消费端
std::string Consume() {
    if (ready_.load(std::memory_order_acquire)) {  // ③ 先读标志
        return data_;                    // ④ 读到完整数据
    }
    return "";
}

// ❌ 多个相关变量用多个 atomic——仍然有竞争,回到锁
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

# 10.4 线程局部存储 【必须】

// ✅ 函数内 thread_local 可自由使用
const Foo& GetThreadLocalFoo() {
  thread_local Foo foo = CreateFoo();
  return foo;
}
1
2
3
4
5

# 10.5 性能优化

// ✅ 容器预分配
std::vector<int> v;
v.reserve(1000);              // 已知大小时预分配

// ✅ std::string_view 替代 const std::string&(C++17)
void Process(std::string_view input);  // 零拷贝,兼容 const char* 和 std::string

// ✅ 循环中用 ++it 而非 it++
for (auto it = v.begin(); it != v.end(); ++it) { }

// ✅ 按 const 引用传参,避免大对象拷贝
void ProcessUser(const User& user);   // 传引用
void SetName(std::string name);        // 需要拷贝时传值 + move

// ✅ 返回值优化(RVO / NRVO):放心 return 局部对象
std::vector<int> BuildVector() {
    std::vector<int> v(1000);
    return v;                           // ✅ 编译器自动移动或原地构造
}
// ❌ 不要写 return std::move(v); —— 反而阻止 RVO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 10.6 缓存友好的数据结构 【推荐】

// ✅ 紧凑内存布局:数组优于链表(cache 友好)
// ❌ 分散在堆上的链表每一跳都可能 cache miss
struct Point { float x, y, z; };
std::vector<Point> points(10000);       // 连续内存,预取友好

// ✅ 结构体字段按访问频率排序(热数据放一起)
struct HotCold {
    int freq_accessed_;                  // 热点数据:前面
    long padding1_[64];
    char rarely_used_[256];             // 冷数据:后面
};

// ✅ 避免伪共享(false sharing)
struct alignas(64) PaddedCounter {      // 独占 cache line
    std::atomic<long> cnt{0};
};
// 或 C++17:
// alignas(std::hardware_destructive_interference_size)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 11.工具链与自动化

# 11.1 静态代码检查

工具 用途 集成
clang-format 代码格式化 保存时自动
clang-tidy 静态分析、风格检查 CI / IDE
cpplint Google 风格检查 CI
Cppcheck Bug 检测 CI
AddressSanitizer 内存错误检测 单元测试

# 11.2 clang-format 配置

# .clang-format
BasedOnStyle: Google
ColumnLimit: 120
IndentWidth: 2
AccessModifierOffset: -1
1
2
3
4
5

# 11.3 CI 集成

# GitHub Actions 示例
- name: Code Quality
  run: |
    clang-format --dry-run --Werror src/**/*.cc  # 格式检查
    clang-tidy src/**/*.cc -- -std=c++17         # 静态分析
    cmake --build . --target test                 # 单元测试
1
2
3
4
5
6

# 11.4 编译期防御 【推荐】

// ✅ static_assert:编译期保证
static_assert(sizeof(int) >= 4, "int must be at least 32 bits");
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");

// ✅ [[nodiscard]]:返回值不可丢弃(C++17)
[[nodiscard]] bool TryLock();
TryLock();                        // ⚠ 编译警告:忽略了返回值

// ✅ = delete:禁止拷贝
class NonCopyable {
 public:
  NonCopyable(const NonCopyable&) = delete;
  NonCopyable& operator=(const NonCopyable&) = delete;
};

// ✅ explicit:防止隐式转换
explicit Timer(std::chrono::milliseconds ms);

// ✅ 编译选项(CMake)
target_compile_options(myapp PRIVATE
    -Wall -Wextra -Wpedantic -Wshadow
    -Wnon-virtual-dtor -Wold-style-cast
    -Werror)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 11.5 pre-commit 钩子 【推荐】

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/mirrors-clang-format
    rev: v17.0.6
    hooks:
      - id: clang-format
        types_or: [c++, c]
1
2
3
4
5
6
7
# 安装
pip install pre-commit
pre-commit install            # 每次 commit 前自动执行
pre-commit run --all-files    # 手动全量检查
1
2
3
4

# 12.代码审查清单

每次 Code Review 时,按以下清单逐项检查:

## 命名
- [ ] 类名大驼峰、变量全小写下划线、常量 k 开头
- [ ] 不存在拼音、单字母、含义模糊的命名
- [ ] 成员变量有下划线后缀 (_)

## 头文件
- [ ] 有头文件保护 (#pragma once 或 #define)
- [ ] #include 顺序正确
- [ ] 无多余的前置声明

## 格式
- [ ] 缩进统一(2 空格)
- [ ] 大括号 K&R 风格
- [ ] 单行不超过 120 字符

## 类设计
- [ ] 构造函数中无虚函数调用
- [ ] 单参数构造函数有 explicit
- [ ] 数据成员 private
- [ ] 优先组合而非继承

## 内存与安全
- [ ] 无裸 new/delete(用智能指针)
- [ ] 资源管理用 RAII
- [ ] 无越界访问、无悬空指针嫌疑
- [ ] 加锁顺序一致
- [ ] 移动构造/赋值标记 noexcept

## 现代 C++
- [ ] 用 nullptr 而非 NULL
- [ ] 用 auto 简化冗长类型
- [ ] 用 override 标记虚函数覆盖
- [ ] 用 std::optional 表示可选值
- [ ] 用 std::string_view 传只读字符串
- [ ] 用 enum class 替代 C 风格 enum
- [ ] 用 [[nodiscard]] 标记不可忽略的返回值

## 性能
- [ ] 循环外声明的对象在循环内是否合理
- [ ] 容器预分配了合理容量
- [ ] 大对象按引用传递
- [ ] 无 return std::move(...) 阻止 RVO
- [ ] 结构体布局缓存友好(热字段在前)

## 测试
- [ ] 核心逻辑有单元测试
- [ ] 正常和异常路径都覆盖
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

# 13.常见陷阱速查

以下陷阱即使在有经验的开发者中也常见,值得定期回顾。

# 13.1 生命周期陷阱

# 陷阱 描述 正解
1 返回局部引用 const std::string& f() { string s = ...; return s; } 按值返回
2 auto 剥引用 auto x = GetRef(); 发生拷贝 auto& x = GetRef();
3 range-for 拷贝 for (auto x : v) 拷贝每个元素 for (const auto& x : v)
4 lambda 捕获悬空 [this] { ... } 异步执行时 this 已析构 [*this](C++17)
5 const char* 临时 auto p = (std::string("a") + "b").c_str(); 先接住 string 对象

# 13.2 类型陷阱

# 陷阱 描述 正解
1 有符号/无符号比较 int(-1) < size_t(0) 为 false std::cmp_less(C++20)/ 显式转换
2 vector<bool> 不是容器 auto b = v[0]; 是 proxy,不是引用 用 deque<bool> 或显式 bool
3 整数提升 uint16_t a=1, b=2; auto c=a*b; 结果是 int 注意 <cstdint> 和提升规则
4 构造 vs 函数声明 Foo f(Bar()); 声明了一个函数 Foo f{Bar()} / Foo f(Bar{})

# 13.3 性能陷阱

# 陷阱 描述 正解
1 std::endl 频繁 flush std::cout << x << std::endl; std::cout << x << '\n';
2 push_back 不 reserve 反复 reallocate + copy v.reserve(N);
3 传值大对象 void f(std::string s) 每次拷贝 void f(const std::string& s) 或 string_view
4 return std::move(v) 阻止 NRVO return v;
5 map 遍历用 [] for(...) map[k] 会插入新元素 用 find() 或 at()
6 虚函数在热循环 每次调用都查 vtable 用模板/CRTP 消除虚函数
#C++#代码规范
上次更新: 2026/06/17, 09:06:19
技术规范
Java编程代码规范指南

← 技术规范 Java编程代码规范指南→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式