title: Google 开源项目风格指南(中文版) original_path: /工作相关/zh-google-styleguide-20201118.pdf file_size: 1.51 MB total_pages: ~177 author: Google (Benjy Weinberger, Craig Silverstein 等) source_repo: github.com/zh-google-styleguide processed_date: 2026-08-31 method: markitdown 文本提取(结构化重写,非逐字搬运) status: done covers_languages: [C++, Objective-C, Python, JSON, Shell, JavaScript]
Google 开源项目风格指南(中文版)
一份整合性参考笔记。源 PDF 包含 Google 官方维护的 C++ / Objective-C / Python / JSON / Shell 五大风格指南,2020-11-18 版本(C++ 指南 4.45 版,Python 指南 2.6 版)。
中文版并非 Google 官方项目,由国内程序员社区翻译维护;英文版是 Google 内部使用的风格指南。
在线版:zh-google-styleguide GitHub
配套工具:
cpplint(C++ 风格检查器)、google-c-style.el(Emacs 配置)、yapf(Python 自动格式化)
指南一:C++ 风格指南(最详尽,77 页)
1. 头文件
自给自足:每个 .h 文件应能独立编译,不依赖其他头文件的隐式包含。
头文件保护:#ifndef FOO_BAR_BAZ_H_ / #define FOO_BAR_BAZ_H_ / #endif,命名 = <项目>_<路径>_<文件>_H_。
避免前置声明:#include 永远比前置声明安全。前置声明的隐性危害:
- 隐藏依赖关系 → 头文件改动后用户代码跳过重新编译
- 可能被库后续改动破坏(如添加默认参数、扩展类型)
- 对
std::命名空间符号的前置声明行为未定义 - 极端情况下甚至会改变代码语义(重载解析变化)
只有当 #include 真正拖慢编译时才考虑前置声明;类模板优先用 #include。
内联函数规则:函数 ≤ 10 行才考虑内联。绝不要内联析构函数(隐式调用基类和成员析构,实际比表面长得多)。含循环 / switch 的函数内联常常得不偿失。
#include 顺序(增强可读性、暴露隐藏依赖):
1. 本文件对应的 .h(cc 文件)
2. C 系统文件
3. C++ 系统文件
4. 其他库的 .h
5. 本项目内的 .h
每类之间空行分隔,同类按字母排序。禁止使用 . 和 ..。
译者建议(实战可借鉴):
#include空行分隔相关头文件是个好习惯- 类内函数默认会自动内联,所以不必显式标记;非内联函数定义放
.cc - 标准化函数参数顺序(输入在前,输出在后)
2. 作用域
.cc文件:鼓励匿名命名空间或static声明(不要污染外部)- 命名空间:禁止
using-directive(污染整个命名空间),禁止inline namespace(迷惑性) - 禁用全局函数:用
static成员函数或命名空间内的非成员函数代替 - 变量声明:尽可能缩小作用域,声明时立即初始化(C++17 鼓励)
- 静态存储 POD 全局变量:禁止(POD = Plain Old Data,不含构造/析构;含副作用的初始化顺序不可控)
不要在构造函数中做太多逻辑相关的初始化(难以测试、构造失败难以报错)。考虑用工厂函数或 Init() 方法。
3. 类
- 数据成员:声明为
private(除非static const)。提供访问器而非直接暴露 - 声明顺序:
public→protected→private;每类内顺序:类型 → 常量 → 构造函数 → 析构函数 → 成员函数 → 数据成员 - 智能指针:
scoped_ptr和auto_ptr已过时;用std::unique_ptr和std::shared_ptr(C++11) - 操作符重载:慎用,保持与内置类型一致语义;不要提供用户自定义字面量除非必要
4. 函数
- 按引用传递的所有参数必须加
const(除了output参数用非常量引用、局部变量按值) - 右值引用:只在定义移动构造函数/赋值时使用,不要使用
std::forward - 函数重载:要让读者一看调用点就明白调的是哪个重载,避免隐式转换触发的意外匹配
- 默认参数:禁止使用(少数极端情况除外);优先用函数重载
- 变长数组和
alloca():禁止使用
5. 其他 C++ 特性
const使用:能用就用;C++11 起推荐constexpr定义真常量constexpr:可以定义浮点常量、用户自定义常量;但别想靠constexpr强制内联- 异常 (Exceptions):Google 不用 C++ 异常(历史决策)。理由:异常处理会让控制流变复杂,老代码基础上引入异常很困难;构造函数失败必须用工厂函数或
Init()模式处理
6. 命名约定
| 类型 | 规则 | 示例 |
|---|---|---|
| 文件名 | 全小写 + _ 或 -(无约定时优先 _) | my_useful_class.cc |
| 类型名 | PascalCase 无下划线 | MyExcitingClass, MyExcitingEnum |
| 变量名 | 小写 + 下划线;类成员末尾加 _;结构体成员不加 | my_exciting_local_var, my_member_var_ |
常量 (constexpr/const) | k 开头 + PascalCase | kDaysInAWeek = 7 |
| 函数名 | PascalCase;取值/设值函数与变量名匹配 | MyExcitingMethod(), set_my_member_var() |
| 命名空间 | 小写;顶级名 = 项目名 | websearch::index::frobber_internal |
| 枚举值 | 优先 kEnumName,旧代码可用 ENUM_NAME | enum UrlTableErrors { kOK, kErrorOutOfMemory } |
| 宏 | 尽量别用,用就 MY_MACRO_THAT_SCARES_SMALL_CHILDREN |
缩写当单词处理:StartRpc() 不是 StartRPC()。类成员末尾 _ 而非常规 Python 的 _ 前缀——这是 Google C++ 的特殊约定,Python 风格不同。
取值/设值函数:
int count() const;
void set_count(int count);7. 注释
- 每个类、函数、关键变量都要注释做什么(API 注释,公开头文件)
- 函数实现内注释为什么(不重复代码做什么)
- 行注释
//比块注释/* */常用 - TODO 用
TODO(name)格式(TODO前缀让 IDE 能识别) - 别在注释里写代码变更历史(用版本控制)
8. 格式
- 行宽:80 字符(C++ 历史约定)
- 缩进:2 空格,不用 Tab
{}独立成行(不挂行尾)- 函数声明/定义:返回类型单独一行,函数名顶格
- 函数参数太长:每参数一行,运算符(
const &)留在行尾 - 大括号:左大括号前换行;
if单行也加大括号 switch必须有default(即使是空break)- 指针声明
char *c(星号靠左)和char* c都可,但同一项目一致 - 预处理缩进不强制
示例函数声明:
// 这是行注释
int MyFunction(int a_count, // 参数对齐
const std::string& a_name) {
...
}9. 规则特例
任何规则都有例外,但必须能给出充分的理由。
指南二:Python 风格指南(41 页,版本 2.6)
语言规范
-
Lint:用
pylint跑代码。抑制警告用# pylint: disable=reason行注释(不要全局抑制) -
导入规则:
import x(包/模块)from x import y(y 是模块名,不带前缀)from x import y as z(同名或过长时)- 禁止
import y as z(除非z是通用缩写如np) - 禁止相对导入(用完整包名)
- 例外:
typing和six.moves可单独导入
-
包:新代码必须用完整包名导入每个模块(避免搜索路径冲突)
-
异常:允许使用异常(C++ 风格禁用,Python 风格鼓励)
-
全局变量:避免;用
_前缀表示模块内部(_CACHED_DO_NOT_USE) -
列表推导:简单场景 OK;多条件 / 多层嵌套请用循环
-
默认迭代器:用
for item in list而非for i in range(len(list));需要下标用enumerate() -
生成器:需要时才用(节省内存),不要为”看起来酷”用
-
Lambda:单行且赋给变量;表达式超过 60-80 字符就别用 lambda,改用
def -
条件表达式:仅在简单情况用(
if部分);复杂情况用完整if/else -
True/False 比较:永远不要
==,直接if x:或if not x: -
Type Annotations(PEP 484):鼓励使用,提升可读性 + IDE 提示 + 类型检查
风格规范
-
行宽:79 字符(C++ 80,Python 79)
-
缩进:4 空格,不用 Tab
-
空行:顶级定义间 2 行,类内方法间 1 行
-
文件编码:UTF-8
-
导入顺序:标准库 → 第三方 → 本项目;每组内按字母排序;组间空行
-
字符串:用双引号
"",单引号''当内容含双引号时用 -
TODO格式:# TODO(username)— 风格和 C++ 一致 -
主入口:
def main(): ... if __name__ == '__main__': main() -
类:继承
object(Python 2;3 可省略)。装饰器@classmethod/@staticmethod用@单独一行 -
命名:
类型 规则 示例 模块/包 小写 + 下划线(包短,模块长) module.py,my_package类 PascalCase MyExcitingClass异常 PascalCase + Error后缀MyExcitingError函数/变量 小写 + 下划线 my_exciting_function(),my_exciting_local_var常量 大写 + 下划线(与 C++ 不同!) MY_EXCITING_CONSTANT = 27内部(模块/类/函数) _前缀_internal_function()避免命名 单字符(除 i,e)、连字符、保留字 -
注释:模块有 docstring;公开 API 函数有 docstring;不要描述”做什么”(除非复杂),描述参数/返回值/异常(Google docstring 风格:
Args:/Returns:/Raises:) -
字符串避免
\+续行(难看且易出错),用括号隐式续行:long_string = ("这是一段很长的" "字符串内容") -
/,%vsformatvs f-string:单一%可用,但多参数时优先用 f-string(PEP 498,Python 3.6+) -
多行构造:列表/字典/集合换行时最后一个元素后加逗号(避免版本控制 diff 噪音)
指南三:Shell 风格指南(19 页)
-
文件后缀:
.sh或.bash(别用.bashrc等系统文件名) -
shebang:
#!/bin/bash而非#!/bin/sh -
缩进:2 空格,不用 Tab
-
行宽:80 字符
-
for用文件名循环:for file in *.txt; do echo "$file" done -
$@vs$*:永远用"$@"(带引号,正确处理含空格的参数) -
命名:变量小写下划线;常量全大写下划线;函数小写下划线;模块内私有前缀
_ -
if/for/while:用[[ ]]不用[ ](bash 扩展);始终带引号避免 glob 展开:# bad if [ $my_var = "test" ]; then ... # good if [[ "${my_var}" == "test" ]]; then ... -
检查命令是否存在:
command -v foo >/dev/null 2>&1,不用which(行为不统一) -
返回值检查:成功
return 0,失败return non-zero;调用方用if cmd; then ...或检查$? -
local陷阱:local foo="$(my_func)"中$?是local的退出码而非my_func的,要单独存:local foo foo="$(my_func)" || return $? -
避免
eval:调试困难、安全风险 -
管道:复杂管道可拆成命名函数提升可读性;不用
cat file | grep(直接grep ... file)
指南四:Objective-C 风格指南(约 30 页)
- 命名:方法名
lowercaseCamelCase,类名UpperCamelCase(与 C++/Java 一致);常量用k前缀(与 C++ 一致) - 空格:方法调用和定义中,
-/+与返回类型间有空格,方法名后无空格- (void)doSomethingWith:(NSString *)foo { ... } - 属性:用
@property声明;访问器自动合成;属性特性nonatomic, strong, copy等 - 注释:用
///或/** ... */(Xcode 兼容) - Cocoa 模式:委托 (
delegate)、KVO、Notification;不在 init/dealloc 中访问属性(避免 KVO 副作用)
指南五:JSON 风格指南(较短,节选核心)
- 属性名:双引号字符串;蛇形命名(
first_name),不用驼峰 - 值类型:string / number / boolean / null / object / array;不用注释、不用 undefined
- 日期:字符串格式(ISO 8601:
"2014-08-29T14:45:00Z"),不用时间戳数字 - 数字:不用前导 0(八进制陷阱);大数用
1e6而非1000000;精度限制时考虑字符串 - Null:语义清楚时用;避免
null/""/0三种空值 - 枚举:用 string 不用 number(可读性 + 演化友好)
- 嵌套:浅一些好;超过 3 层考虑拆字段或拆分结构
- 字段顺序:必填在前,可选在后;高频访问在前
跨指南的通用原则
- 一致性优先于个人偏好:规则可以商榷,但项目内必须一致
- 自动化检查:C++ 用
cpplint,Python 用pylint+yapf,Shell 用shellcheck - 命名空间的语言差异:
- C++:常量
kFoo,类成员_foo - Python:常量
FOO,内部_foo - Shell:常量
FOO,函数foo()
- C++:常量
- 头文件/导入的依赖暴露:C++ 通过
#include顺序,Python 通过完整包名导入 - 代码即文档:注释解释为什么不解释做什么;好的命名减少注释需求
- 避免过度设计:C++ 不用 RTTI、异常、C 风格数组、默认参数;Python 不滥用 lambda、生成器
- 缩进差异(注意!):C++ 2 空格,Python 4 空格,Shell 2 空格——不要把 Python 项目写成 2 空格
与本知识库其他笔记的关联
- 《重构》-改善既有代码的设计 — C++/Java 重构实践
- 《程序员修炼之道》-从小工到专家 — 编码哲学层的指南
- SICP — 函数式编程的命名与抽象风格
实用资源
- 在线阅读:zh-google-styleguide GitHub
- cpplint:github.com/cpplint/cpplint
- yapf:github.com/google/yapf
- shellcheck:www.shellcheck.net
待确认 / 局限性
- 本笔记基于 2020-11-18 版本的中文翻译快照;C++ 后续重大更新(如 Abseil 风格、C++20 特性)未涵盖
- JavaScript 和 XML 文档格式风格指南未在本 PDF 中包含(项目仍在招募译者)
- Objective-C 部分因 Apple 生态转向 Swift,参考价值有限
- 翻译版可能与英文原版有微小差异,遇到歧义建议查阅英文原版