1. 通用原则
总体规范参考 Google 规范,略有改动 详见 $\rightarrow$ https://google.github.io/styleguide/cppguide.html
使用或借助 AI 写代码请务必自己理解每个代码都在干什么
行宽不超过 80 字符,设置 vscode 行宽 80 字符线:
Ctrl(Cmd)+,或者 File $\rightarrow$ Preferences $\rightarrow$ Settings- 搜索:
ruler,Editor: Ruler一栏编辑 settings.json:
1{
2 "editor.rulers": [80] // 原有配置的基础上添加这一行
3}
.cc 文件与.cpp 文件本质没区别,统一使用.cpp 后缀
代码可读性第一,避免过度优化或炫技
变量、函数、类名应自解释,避免无意义缩写(如 a, tmp1),除非是广泛接受的缩写(如 id, url, cmd)
错误处理必须显式,不能忽略可能的异常、错误码或返回值
全英文注释:统一,正式,美观,且标点只使用半角标点(英文字符)
- 标点(
:,;.?!)前不加空格,后加一个空格,
(()[]{}""'')外侧加空格内测不加
(-_)前后不加空格,其余所有运算符前后都有空格 - 在函数名和左括号之间不要留空格。在参数列表的逗号后留一个空格。
int add(a, b); - 避免在圆括号、方括号或花括号内部紧邻留空格。
list[1:5]dict = {'key': value}
- 标点(
声明与初始化尽可能的靠近,如
1int i = function();
整体尽量避免使用宏
以下标准可以存在极少的例外
2 C++
2.1 头文件,声明规范
2.1.1 显式包含代替隐式包含:
1// foo.h
2#include "bar.h" // 因为参数中使用了 Bar,所以必须包含 Bar 的定义
3void useBar(Bar b);
4
5// foo.cpp
6#include "foo.h"
7// 即使 foo.h 已经包含了 bar.h,这里最好也显式包含一次(根据规则后半部分)
8#include "bar.h" // 显式包含,表明我依赖 Bar
9
10void useBar(Bar b) {
11 // ... 实现代码
12}
2.1.2 尽可能避免前向声明:
1// b.h:
2struct B {};
3struct D : B {};
4
5// good_user.cc:
6#include "b.h"
7void f(B*);
8void f(void*);
9void test(D* x) { f(x); } // 调用 f(B*)
2.1.3 include 的名称与顺序:
1// C/C++标准库 系统 第三方库使用尖括号
2// 其他使用引号
3// 顺序如下
4#include "foo/server/fooserver.h" // 主要关联头文件
5
6#include <sys/types.h> // 系统头文件
7#include <unistd.h>
8
9#include <string> // 标准库头文件
10#include <vector>
11
12#include "base/basictypes.h" // 项目内部头文件
13#include "foo/server/bar.h"
14#include "third_party/absl/flags/flag.h"
2.1.4 变量声明
最小化作用域 (Minimize Scope): 局部变量应该在使用前的最小可能作用域内声明和定义。
声明时初始化 (Initialize on Declaration): 变量在声明时必须进行初始化。
尽可能使用: 尽可能使用 const 或 constexpr 来声明变量。不要使用宏!
指针和引用: 将 * 和 & 放在类型旁边,而不是变量名旁边。
只有在能够提高可读性或避免冗余类型名时才使用
auto。
2.1.5 函数声明
参数顺序:输入参数(
const或值),输入输出参数,输出参数默认参数:必须将它们放在声明中(即在 .h 文件中),而不是定义中(即在 .cc 文件中)。
2.2 命名规范
2.2.1 命名空间规范
不使用缩进: 命名空间内的代码块不进行额外的缩进。
花括号:
{放在命名空间名称的同一行。命名空间结束注释: 命名空间结束的
}后面必须加上一行注释,指明结束的是哪个命名空间。
1namespace mynamespace {
2// 空行
3// 代码... (Code...)
4
5class MyClass {
6public:
7 void DoSomething();
8};
9
10void MyClass::DoSomething() {
11 // 实现细节... (Implementation details...)
12}
13
14} // namespace mynamespace // 在命名空间的结尾处添加注释
15 // 注释内容与命名空间名称完全一致
2.2.2 命名空间使用规范
禁止
using-directive: 绝对不要使用using namespace foo;这样的指令。禁止
inline namespace: 不要使用内联命名空间。
1// 明确禁止,污染命名空间
2using namespace abc;
- 明确使用
std::: 对于 C++ 标准库的符号,必须始终使用std::前缀,例如std::string,std::vector。
1// 允许:仅引入单个名称
2using foo::bar;
1// 匿名命名空间示例 (Example Unnamed Namespace in a .cc file)
2
3namespace {
4// 这里的变量和函数仅对当前 .cc 文件可见
5int internal_counter = 0;
6
7void internal_helper_function() {
8 // ...
9}
10} // namespace
2.2.3 各类型命名规则
| 类型 | 规则 | 示例 |
|---|---|---|
| 头文件 | 小写 + 下划线 | my_useful_class.h |
| 类、结构体、枚举、类型别名 | 单词首字母大写 | ProcessData, MaxVelocity |
| 函数、方法 | 小写 + 下划线 | do_some_thing() |
| 宏 | 全大写 + 下划线 | A_INT_ABC |
| 常量 | 首单词首字母小写,其余单词首字母大写 | kBufferSize |
| 类成员 | 全小写且结尾加下划线 | name*, user_count* |
2.3 格式与缩进
1class Sensor {
2 public: // 单个空格
3 void readData() { // 单个空格
4 if (isConnected_) { // 两个空格
5 fetchData(); // 两个空格
6 }
7 }
8
9 private: // 单个空格
10 bool isConnected_{false};// 单个空格
11};
2.4 内存与资源管理
2.4.1 RAII 原则
将资源的生命周期绑定到对象的生命周期上
资源获取: 资源(如内存、文件句柄、锁、网络连接)在对象构造时获取。
资源释放: 资源在对象析构时自动释放。
必须使用 RAII 封装器来管理互斥锁 (
std::mutex)。
1void GoodFunction() {
2 std::vector<int> data(100); // 自动管理内存
3 // 即使抛出异常,vector 析构函数也会自动释放内存
4}
2.4.2 智能指针
- 绝对禁止使用原始指针(
new/delete)进行内存管理。必须使用智能指针来管理动态分配的对象。
1auto ptr1 = std::make_unique<MyClass>(arg1, arg2);
2auto ptr2 = std::make_shared<MyClass>(arg1, arg2);
2.4.3 禁止使用宏定义代替常量或内联函数(除非必要)
- 具名常量
1constexpr int kDefaultBufferSize = 1024;
2const double kPi = 3.1415926535;
- 枚举常量
1enum class ErrorCode {
2 kSuccess = 0,
3 kTimeout = 1,
4 kHardwareFailure = 2
5};
- 内联函数
只有当函数非常短(不超过 10 行)时,才允许在头文件中定义
1template <typename T>
2inline T Max(T a, T b) {
3 // 行为与普通函数一致,不会重复求值
4 return (a > b) ? a : b;
5}
2.5 异常与错误处理
2.5.1 禁止使用 C++的异常
- 完全避免
try,catch, 和throw关键字
2.5.2 可预期的运行时错误: Status 或错误码
- 函数调用失败但程序可以继续运行的情况,返回错误状态,调用者决定恢复或传播。
1// 使用 Status 对象作为返回值
2absl::Status OpenFile(const std::string& path);
3
4// 调用端处理:
5absl::Status status = OpenFile("/path/to/file");
6if (!status.ok()) {
7 LOG(ERROR) << "Failed to open file: " << status.message();
8 // 根据错误类型进行恢复或直接返回
9 return status;
10}
2.5.3 不可恢复的编程错误: 断言 (Assertions)
- 用于检查那些如果失败则表明代码逻辑存在 bug 的条件。
1// 使用 CHECK 宏检查不可恢复的编程错误
2void ProcessData(const std::vector<int>& data) {
3 // 检查 data 向量不应该为空。如果为空,说明调用者或上游代码有 Bug。
4 CHECK(!data.empty()) << "Input data must not be empty.";
5
6 // ... 正常逻辑
7}
2.6 注释
2.6.1 公告接口
只在声明处注释: Doxygen 注释块只放在头文件(.h)的声明上方。不要在实现文件(.cc)中重复。
避免冗余: Doxygen 标签的内容应该为使用代码的人提供价值,而不是重复代码中显而易见的信息。例如,如果函数名为 CalculateSum,就没必要在 @brief 中写“计算总和”。
公共接口使用 Doxygen 风格注释:
1/**
2 * @brief 计算两个整数的和。
3 *
4 * 该函数执行简单的算术加法,不会产生溢出检查。
5 *
6 * @param a 第一个加数。
7 * @param b 第二个加数。
8 * @return 整数 a 和 b 的总和。
9 */
10int Add(int a, int b);
| 标签 (Tag) | 作用 (Purpose) | 示例 (Example) |
|---|---|---|
@brief | 简要说明。用于对代码实体进行简短的一句话描述,通常作为文档的标题。 | /// @brief 初始化连接池。 |
@param | 参数描述。描述函数的输入参数。 | * @param count 要创建的连接数量。 |
@return | 返回值描述。描述函数的返回值。 | * @return 成功返回 Status::OK。 |
@note | 注意事项。用于描述使用时的特殊注意事项或约束。 | * @note 该操作不是原子性的。 |
@warning | 警告。用于描述潜在的危险或已知的限制。 | * @warning 传入 null 会导致崩溃。 |
@tparam | 模板参数描述。描述模板函数的类型参数。 | * @tparam T 容器中元素的类型。 |
2.6.2 文件头部注释
文件内容描述: 紧随其后,对文件内容进行简要描述。
.h 文件: 描述其中声明的类和函数的作用、用途以及使用方式。
.cpp 文件: 描述实现细节、复杂算法或任何需要注意的实现决策。
2.6.3 注释内容
描述性而非祈使性: 注释应该描述代码是做什么的(例如:“Opens the file”),而不是命令它去做什么(例如:“Open the file”)。
解释原因: 最好的注释是解释为什么这样做,特别是对于非显而易见的、复杂的或有陷阱的代码。
TODO:描述必须清晰、完整, 能够解释为什么需要这个 TODO(临时解决方案、已知缺陷、未来工作),以及完成它需要做什么。
1// TODO(责任人): 详细描述待办事项
3. 范式
注意:实际编辑的时候请使用英文注释
范式内包含很多无意义注释(为了理解规范),实际编辑时不要添加
3.1 文件树
1.
2├── main.cpp
3├── sensor_data.cpp
4└── sensor_data.h
3.2 main.cpp
1// main.cpp
2#include "sensor_data.h" // Project internal header
3
4#include <iostream> // Standard library header
5#include <string> // Standard library header
6
7// Use constexpr instead of macros for constants
8// Constant naming: k prefix, PascalCase
9constexpr int kMainReturnCodeSuccess = 0;
10
11// Allowed: only introducing a single name
12using sensor::SensorData;
13
14int main() {
15 // Declaration and initialization as close as possible
16 SensorData sensor_instance;
17
18 // Variable names should be self-explanatory
19 const int requested_data_size = 50;
20
21 // Function/method call
22 std::string data_output = sensor_instance.get_data(requested_data_size);
23
24 std::cout << "Data received: " << data_output << std::endl;
25
26 sensor::reset_sensor_connection();
27
28 // If using an enum class, scope is required (ErrorCode::kSuccess)
29 // Punctuation (:) before no space, after one space
30 // Use the k-prefixed constant for comparison
31 if (static_cast<int>(sensor::ErrorCode::kSuccess) == kMainReturnCodeSuccess) {
32 // Punctuation (.) before no space, after one space
33 std::cout << "Program finished successfully." << std::endl;
34 }
35
36 return kMainReturnCodeSuccess;
37}
3.3 sensor_data.cpp
1// sensor_data.cpp
2// Line width does not exceed 80 characters
3#include "sensor_data.h" // Primary associated header
4
5#include <algorithm> // Standard library header for std::min
6#include <iostream> // Standard library header
7#include <unistd.h> // System header
8
9namespace sensor {
10
11// Function naming: snake_case
12std::string SensorData::get_data(int data_size) {
13 // Avoid meaningless abbreviations, such as 'a' or 'tmp'
14 const int current_size = std::min(data_size, kMaxDataSize);
15
16 // Operators have spaces before and after, parentheses have space on the outside and no space on the inside
17 if (current_size < 10) {
18 // Error handling must be explicit (this is only an example)
19 // The actual code should use absl::Status
20 std::cerr << "ERROR: Data size is too small." << std::endl;
21 return "";
22 }
23
24 // Class member access
25 user_count_++;
26
27 // Use named constants instead of macros
28 std::string result = name_ + ": " + std::to_string(current_size);
29 return result;
30}
31
32void reset_sensor_connection() {
33 // All English comments: unified, formal, and punctuation only includes half-width punctuation
34 // The connection is resetting now by sleeping for 1 second.
35 // Note: For production code, the return value of sleep should be checked
36 // to ensure error handling is explicit.
37 sleep(1);
38}
39
40} // namespace sensor
3.4 sensor_data.h
1// sensor_data.h
2#ifndef FOO_SENSOR_DATA_H_
3#define FOO_SENSOR_DATA_H_
4
5#include <string> // Standard library header
6
7// Constant naming: k prefix, first word lowercase, rest of words PascalCase
8constexpr int kMaxDataSize = 80;
9
10// Enum naming: PascalCase
11// Use strong-typed enum (enum class)
12enum class ErrorCode {
13 kSuccess = 0,
14 kTimeout = 1,
15 kHardwareFailure = 2
16};
17
18namespace sensor { // Namespace: all lowercase
19
20// Class/Struct naming: PascalCase
21class SensorData {
22 public:
23 /**
24 * @brief Retrieves the current sensor reading.
25 *
26 * This is a filtered and calibrated value.
27 *
28 * @param data_size The desired size of the returned data.
29 * @return The processed sensor data as a string.
30 */
31 // Function naming: snake_case
32 std::string get_data(int data_size);
33
34 private:
35 // Class member naming: all lowercase, ending with underscore
36 std::string name_{"DefaultSensor"}; // Declaration and initialization near usage
37 int user_count_{0};
38};
39
40// Function naming: snake_case
41void reset_sensor_connection();
42
43} // namespace sensor
44
45#endif // FOO_SENSOR_DATA_H_