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 开头 + PascalCasekDaysInAWeek = 7
函数名PascalCase;取值/设值函数与变量名匹配MyExcitingMethod(), set_my_member_var()
命名空间小写;顶级名 = 项目名websearch::index::frobber_internal
枚举值优先 kEnumName,旧代码可用 ENUM_NAMEenum 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
    类PascalCaseMyExcitingClass
    异常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 = ("这是一段很长的"
                   "字符串内容")
  • /, % vs format vs 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 层考虑拆字段或拆分结构
  • 字段顺序:必填在前,可选在后;高频访问在前

跨指南的通用原则

  1. 一致性优先于个人偏好:规则可以商榷,但项目内必须一致
  2. 自动化检查:C++ 用 cpplint,Python 用 pylint + yapf,Shell 用 shellcheck
  3. 命名空间的语言差异:
    • C++:常量 kFoo,类成员 _foo
    • Python:常量 FOO,内部 _foo
    • Shell:常量 FOO,函数 foo()
  4. 头文件/导入的依赖暴露:C++ 通过 #include 顺序,Python 通过完整包名导入
  5. 代码即文档:注释解释为什么不解释做什么;好的命名减少注释需求
  6. 避免过度设计:C++ 不用 RTTI、异常、C 风格数组、默认参数;Python 不滥用 lambda、生成器
  7. 缩进差异(注意!):C++ 2 空格,Python 4 空格,Shell 2 空格——不要把 Python 项目写成 2 空格

与本知识库其他笔记的关联

实用资源

待确认 / 局限性

  • 本笔记基于 2020-11-18 版本的中文翻译快照;C++ 后续重大更新(如 Abseil 风格、C++20 特性)未涵盖
  • JavaScript 和 XML 文档格式风格指南未在本 PDF 中包含(项目仍在招募译者)
  • Objective-C 部分因 Apple 生态转向 Swift,参考价值有限
  • 翻译版可能与英文原版有微小差异,遇到歧义建议查阅英文原版