C++教程(五):C++高手养成之代码规范,如何写出规范优雅的程序
C++代码规范旨在提高代码的可读性、可维护性和一致性,同时减少潜在的错误和代码异常。以下是一些常见的C++代码规范。
1. 命名规范
1. 变量命名
局部变量和函数参数应采用小写字母开头的驼峰命名法(lowerCamelCase),即首字母小写,后续单词首字母大写。类的成员变量也采用 lowerCamelCase 命名,但要在尾部添加下划线 _ 以区别于局部变量和参数。全局变量不推荐使用,若必须使用,需置于命名空间内,并采用全小写字母加下划线分隔的命名方式。静态变量的命名规则与全局变量类似,也采用 lower_snake_case。
2. 常量命名
常量(包括常量表达式和常量变量)的命名应以大写字母 K 开头,并使用 UpperCamelCase 命名法。这样做是为了确保常量易于识别,同时与其他变量区分开。
3. 函数命名
函数名采用 lowerCamelCase 命名法,首字母小写,后续单词首字母大写。函数名称应能清楚描述其功能,通常为动词或动词短语。
4. 类型命名
类、结构体和枚举的名称应使用 UpperCamelCase,即每个单词首字母大写,单词连写。枚举值则采用 kUpperCamelCase 命名风格,与常量的命名方式相同,以便区分枚举值和其他变量。
5. 命名空间
命名空间名应全部小写字母,单词之间用下划线分隔。命名空间的作用是组织代码,避免全局命名冲突,尤其在大型项目中,合理的命名空间设计能够显著提升代码的组织性和可维护性。
6. 宏命名
宏定义(通过 #define 定义的预处理器指令)应使用全大写字母,单词之间用下划线分隔。宏在 C++ 中应尽量避免使用,除非在不得已的场景下才使用。
7. 文件命名
文件名应使用全小写字母,单词之间用下划线分隔。头文件的扩展名为 .h,实现文件的扩展名为 .cc。测试文件的命名规则是将被测试文件的名称加上 _test 后缀,以明确其测试属性。
8. 测试函数
在 Google Test 框架中,测试函数命名通常以 UpperCamelCase 风格,且应清晰描述测试的功能。函数名通常为 TEST 宏定义的类中的方法,用来确保代码的正确性。
9. 命名冲突处理
通过命名空间可以有效避免全局命名冲突。所有代码应当放在命名空间内,尤其在大型项目中,合理的命名空间管理不仅避免冲突,还能提升代码组织性。
10. 函数模板和类模板
模板函数和模板类的命名遵循普通函数和类的命名规则,模板参数通常使用大写字母,如 T、U 等。这样可以提高模板的可读性和一致性。
11. 访问控制和成员函数
类的访问控制符应按 public、protected、private 的顺序出现,以确保访问权限划分清晰。类的私有成员变量和私有函数应遵循 lowerCamelCase 命名法,并以下划线 _ 结尾。
12. 缩写和首字母缩写
命名中应避免使用不常见的缩写,确保代码的易读性。常见的缩写(如 HTML、URL 等)可以大写使用,但在驼峰命名中,只有首单词为缩写时,才应全部大写,否则只需首字母大写。
13. 私有类成员和私有函数
类的私有成员变量和私有函数采用 lowerCamelCase 命名法,并在末尾添加下划线 _。这有助于区分私有成员和其他类型的成员,增强代码的清晰度。
// 头文件和实现文件命名
// 文件:my_class.h
// 文件:my_class.ccnamespace my_project {class MyClass {public:void setValue(int value);int getValue() const;private:int value_;void privateFunction_();
};} // namespace my_project// 常量定义
const int kMaxValue = 100;
const double kPi = 3.14159;// 函数定义
void MyClass::setValue(int value) {value_ = value;
}int MyClass::getValue() const {return value_;
}// 测试文件:my_class_test.cc
#include "my_class.h"
#include <gtest/gtest.h>TEST(MyClassTest, HandlesDefaultInput) {MyClass my_class;my_class.setValue(10);EXPECT_EQ(my_class.getValue(), 10);
}TEST(MyClassTest, HandlesEdgeCase) {MyClass my_class;my_class.setValue(100);EXPECT_EQ(my_class.getValue(), 100);
}
2. 注释规范
注释应简洁明了,解释“为什么”而不是“是什么”。
单行注释使用//,多行注释使用/* ... */。
为函数和类添加详细注释,说明其功能、参数和返回值。
对复杂逻辑的代码段进行详细注释。
使用TODO标记未完成的工作,并添加责任人。
头文件应包含文件作用的说明注释。
// car.h
// 该文件定义了Car类及其相关的函数和属性。/** Car类表示一辆汽车,包含速度和燃料信息。* 它支持更新速度的功能。*/
class Car {
public:// 更新汽车速度// @param new_speed: 新的速度值void updateSpeed(int new_speed);private:int speed_; // 当前速度float fuel_level_; // 当前燃料水平
};// 计算两个整数的和
// @param a: 第一个整数
// @param b: 第二个整数
// @return: 两个整数的和
int add(int a, int b);// TODO(john_doe): 优化此函数以提高性能
void inefficientFunction() {// 目前的实现很慢
}
3. 格式规范
1. 缩进和空格
Google C++ 代码使用 2 个空格进行缩进,而不是使用 Tab 键,以确保在不同编辑器和环境下代码对齐一致。运算符和关键字之间应留空格,例如在赋值、比较和算术运算符的两侧都应加空格。函数调用和参数之间不应有空格,参数之间则用逗号和空格分隔。
2. 行长度
每行代码的长度应不超过 80 个字符。如果代码行超过 80 个字符,应进行适当的折行,通常是在逻辑运算符或逗号后换行,后续行相对于上一行多缩进 4 个空格。
3. 括号与换行
左大括号应紧随声明或关键字,不应独占一行。右大括号应独立成行。对于单行的控制结构语句,虽然可以省略大括号,但为减少错误风险,建议始终加上大括号。函数定义或声明后的代码块应换行,函数体内的代码应与大括号对齐。每个语句应独立成行,不允许将多个语句写在同一行。
4. 空行
空行用于分隔代码的不同逻辑部分。在不同函数或逻辑块之间,以及函数实现之间,都应留空行,以提高代码的可读性。
5. 头文件顺序
头文件的引入顺序应依次为:C++ 标准库头文件,第三方库的头文件,最后是项目的头文件。不同部分之间用空行隔开。头文件应使用 #pragma once,或传统的 include guard 以防止重复包含。
6. 指针和引用
声明指针和引用时,符号 * 和 & 应紧靠变量名,而不是类型名,以确保一致的代码风格。
#include <iostream>// 计算两个数的和。
// @param a 第一个整数。
// @param b 第二个整数。
// @return 两数之和。
int AddNumbers(int a, int b) {return a + b;
}int main() {int result = AddNumbers(5, 3); // 计算 5 + 3std::cout << "Result: " << result << std::endl;return 0;
}
4. 头文件规范
头文件保护:使用 #pragma once 或者 #ifndef/#define 进行头文件保护,防止重复包含。
头文件和源文件分离:类声明放在头文件 (.h 或 .hpp) 中,类的实现放在源文件 (.cpp) 中。
#pragma once#ifndef MYCLASS_H
#define MYCLASS_Hclass MyClass {// 类定义
};#endif // MYCLASS_H
5. 智能指针
避免直接使用裸指针,优先使用 std::unique_ptr 和 std::shared_ptr 来管理资源,防止内存泄漏。
std::unique_ptr<Car> car = std::make_unique<Car>();
6. 异常处理
使用 try-catch 处理异常,而不是通过返回错误码。
不要捕获所有异常(如 catch (...)),应捕获特定类型的异常。
try {// 可能抛出异常的代码
} catch (const std::exception& e) {std::cerr << e.what();
}
7. 现代C++特性
充分利用现代C++特性(如auto、lambda表达式、nullptr等)提高代码的简洁性和安全性。
auto it = myVector.begin();