├── index.rst ├── google-shell-styleguide ├── conclusion.rst ├── index.rst ├── environment.rst ├── contents.rst ├── shell_files_and_interpreter_invocation.rst ├── background.rst ├── comments.rst ├── calling_commands.rst ├── naming_conventions.rst ├── features_and_bugs.rst └── formatting.rst ├── google-javascript-styleguide ├── background.rst ├── contents.rst └── javascript_language_rules.rst ├── google-html-css-styleguide ├── background.rst ├── parting_words.rst ├── css_meta_rules.rst ├── general_style_rules.rst ├── general_meta_rules.rst ├── general_formatting_rules.rst ├── html_formatting_rules.rst ├── css_formatting_rules.rst ├── css_style_rules.rst └── html_style_rules.rst ├── google-objc-styleguide ├── contents.rst ├── patterns.rst ├── comments.rst ├── index.rst ├── naming.rst ├── spacing.rst └── features.rst ├── google-python-styleguide ├── contents.rst ├── background.rst ├── parting_words.rst └── index.rst ├── google-typescript-styleguide ├── contents.rst ├── consistency.rst ├── preface.rst ├── source_organization.rst ├── syntax.rst └── type_system.rst ├── contents.rst ├── google-cpp-styleguide ├── end.rst ├── contents.rst ├── exceptions.rst ├── index.rst ├── magic.rst ├── functions.rst ├── headers.rst ├── naming.rst ├── scoping.rst ├── comments.rst └── classes.rst ├── manually-generate-pdf-document.md ├── .gitignore ├── README.rst ├── conf.py ├── Makefile └── make.bat /index.rst: -------------------------------------------------------------------------------- 1 | README.rst -------------------------------------------------------------------------------- /google-shell-styleguide/conclusion.rst: -------------------------------------------------------------------------------- 1 | 结论 2 | ================================ 3 | 4 | 使用常识并保持一致。 5 | 6 | 请花几分钟阅读在C++风格指南底部的赠别部分。 7 | 8 | -------------------------------------------------------------------------------- /google-javascript-styleguide/background.rst: -------------------------------------------------------------------------------- 1 | 背景 2 | ============== 3 | 4 | 在Google的开源项目中,JavaScript是最主要的客户端脚本语言。本指南是使用JavaScript时建议和不建议做法的清单。 5 | -------------------------------------------------------------------------------- /google-html-css-styleguide/background.rst: -------------------------------------------------------------------------------- 1 | 背景 2 | ================================ 3 | 4 | 本文档定义了HTML/CSS的排版以及风格的规则。旨在促进合作编程、提高代码质量并且支持基本的架构。它适用于原生的HTML和CSS文件,包括GSS文件。只要保证代码的整体质量,就可以很容易地使用工具进行混淆、压缩和合并。 5 | 6 | -------------------------------------------------------------------------------- /google-objc-styleguide/contents.rst: -------------------------------------------------------------------------------- 1 | .. 请确保至少包含基本的 `toctree` 指令. 2 | 3 | .. _objc_contents: 4 | 5 | Objective-C 风格指南 - 内容目录 6 | =============================== 7 | 8 | .. toctree:: 9 | 10 | index 11 | spacing 12 | naming 13 | comments 14 | features 15 | patterns 16 | -------------------------------------------------------------------------------- /google-python-styleguide/contents.rst: -------------------------------------------------------------------------------- 1 | .. 请确保至少包含基本的 `toctree` 指令. 2 | 3 | .. _python_contents: 4 | 5 | Python 风格指南 - 内容目录 6 | ========================== 7 | 8 | .. toctree:: 9 | 10 | index 11 | background 12 | python_language_rules 13 | python_style_rules 14 | parting_words 15 | -------------------------------------------------------------------------------- /google-javascript-styleguide/contents.rst: -------------------------------------------------------------------------------- 1 | .. 请确保至少包含基本的 `toctree` 指令. 2 | 3 | .. _javascript_contents: 4 | 5 | Javascript 风格指南 - 内容目录 6 | ========================================= 7 | 8 | .. toctree:: 9 | 10 | background 11 | javascript_language_rules.rst 12 | javascript_style_rules.rst 13 | -------------------------------------------------------------------------------- /google-html-css-styleguide/parting_words.rst: -------------------------------------------------------------------------------- 1 | 赠言 2 | ======== 3 | 4 | 请与周围保持一致。 5 | 6 | 如果你正在编辑代码,花几分钟时间看看上下文代码的格式,确定他们的编码风格。如果在上下文代码中,算术运算符前后有空格,或注释前后添加了“#”,你也应该这样做。 7 | 8 | 编写这个风格指导的目标是让人们可以专注于“我们在讨论什么”而不是“我们该怎么描述”。我们提供了一些通用的编码规范,大家就可以基于这些规范而继续,但特定情况下的规范也同样重要。如果你在一个文件中添加的代码看上去跟其他代码明显不同,你就把阅读此文件的人的节奏打乱了。避免这种情况出现。 9 | -------------------------------------------------------------------------------- /google-typescript-styleguide/contents.rst: -------------------------------------------------------------------------------- 1 | TypeScript 风格指南 2 | ################################################################################ 3 | 4 | .. toctree:: 5 | :caption: 目录 6 | :numbered: 7 | 8 | preface 9 | syntax 10 | language 11 | source_organization 12 | type_system 13 | consistency -------------------------------------------------------------------------------- /google-python-styleguide/background.rst: -------------------------------------------------------------------------------- 1 | 背景 2 | ================================ 3 | 4 | Python 是 Google主要的脚本语言。这本风格指南主要包含的是针对python的编程准则。 5 | 6 | 为帮助读者能够将代码准确格式化,我们提供了针对 `Vim的配置文件 `_ 。对于Emacs用户,保持默认设置即可。许多团队使用 `yapf `_ 作为自动格式化工具以避免格式不一致。 7 | -------------------------------------------------------------------------------- /google-html-css-styleguide/css_meta_rules.rst: -------------------------------------------------------------------------------- 1 | CSS元规则 2 | =========== 3 | 4 | 分段规则 5 | ---------- 6 | 7 | 组的分段由一段注释完成(可选)。 8 | 9 | 尽可能地用注释来将css分段,段与段之间采用新行。 10 | 11 | .. code-block:: css 12 | 13 | /* Header */ 14 | 15 | #adw-header {} 16 | 17 | /* Footer */ 18 | 19 | #adw-footer {} 20 | 21 | /* Gallery */ 22 | 23 | .adw-gallery {} 24 | -------------------------------------------------------------------------------- /contents.rst: -------------------------------------------------------------------------------- 1 | .. 请确保至少包含基本的 `toctree` 指令. 2 | 3 | .. _contents: 4 | 5 | 内容目录 6 | ======== 7 | 8 | .. toctree:: 9 | 10 | index 11 | google-cpp-styleguide/contents 12 | google-objc-styleguide/contents 13 | google-python-styleguide/contents 14 | google-shell-styleguide/contents 15 | google-javascript-styleguide/contents 16 | google-typescript-styleguide/contents 17 | -------------------------------------------------------------------------------- /google-cpp-styleguide/end.rst: -------------------------------------------------------------------------------- 1 | 11. 结束语 2 | ~~~~~~~~~~~~~~~~ 3 | 4 | 运用常识和判断力, 并且 *保持一致*. 5 | 6 | 编辑代码时, 花点时间看看项目中的其它代码, 并熟悉其风格. 如果其它代码中 ``if`` 语句使用空格, 那么你也要使用. 如果其中的注释用星号 (*) 围成一个盒子状, 那么你同样要这么做. 7 | 8 | 风格指南的重点在于提供一个通用的编程规范, 这样大家可以把精力集中在实现内容而不是表现形式上. 我们展示的是一个总体的的风格规范, 但局部风格也很重要, 如果你在一个文件中新加的代码和原有代码风格相去甚远, 这就破坏了文件本身的整体美观, 也让打乱读者在阅读代码时的节奏, 所以要尽量避免. 9 | 10 | 好了, 关于编码风格写的够多了; 代码本身才更有趣. 尽情享受吧! 11 | -------------------------------------------------------------------------------- /google-cpp-styleguide/contents.rst: -------------------------------------------------------------------------------- 1 | .. 请确保至少包含基本的 `toctree` 指令. 2 | 3 | .. _cpp_contents: 4 | 5 | C++ 风格指南 - 内容目录 6 | ======================================== 7 | 8 | .. contents:: 9 | :backlinks: none 10 | 11 | .. toctree:: 12 | 13 | index 14 | headers 15 | scoping 16 | classes 17 | functions 18 | magic 19 | others 20 | naming 21 | comments 22 | formatting 23 | exceptions 24 | end 25 | -------------------------------------------------------------------------------- /google-shell-styleguide/index.rst: -------------------------------------------------------------------------------- 1 | 扉页 2 | =============================== 3 | 4 | :版本: 1.26 5 | 6 | :原作者: 7 | 8 | .. line-block:: 9 | 10 | Paul Armstrong 11 | 等等 12 | 13 | :翻译: 14 | 15 | .. line-block:: 16 | 17 | `Bean Zhang `_ v1.26 18 | 19 | :项目主页: 20 | 21 | - `Google Style Guide `_ 22 | - `Google 开源项目风格指南 - 中文版 `_ 23 | 24 | -------------------------------------------------------------------------------- /google-shell-styleguide/environment.rst: -------------------------------------------------------------------------------- 1 | 环境 2 | ================================ 3 | 4 | STDOUT vs STDERR 5 | -------------------- 6 | 7 | .. tip:: 8 | 所有的错误信息都应该被导向STDERR。 9 | 10 | 这使得从实际问题中分离出正常状态变得更容易。 11 | 12 | 推荐使用类似如下函数,将错误信息和其他状态信息一起打印出来。 13 | 14 | .. code-block:: shell 15 | 16 | err() { 17 | echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: $@" >&2 18 | } 19 | 20 | if ! do_something; then 21 | err "Unable to do_something" 22 | exit "${E_DID_NOTHING}" 23 | fi 24 | -------------------------------------------------------------------------------- /google-shell-styleguide/contents.rst: -------------------------------------------------------------------------------- 1 | .. 请确保至少包含基本的 `toctree` 指令 2 | 3 | .. _shell_contents: 4 | 5 | =============================== 6 | Shell 风格指南 - 内容目录 7 | =============================== 8 | 9 | .. contents:: 10 | :backlinks: none 11 | 12 | .. toctree:: 13 | :maxdepth: 3 14 | 15 | index 16 | background 17 | shell_files_and_interpreter_invocation 18 | environment 19 | comments 20 | formatting 21 | features_and_bugs 22 | naming_conventions 23 | calling_commands 24 | conclusion 25 | -------------------------------------------------------------------------------- /google-python-styleguide/parting_words.rst: -------------------------------------------------------------------------------- 1 | 临别赠言 2 | ================================ 3 | 4 | **请务必保持代码的一致性** 5 | 6 | 如果你正在编辑代码, 花几分钟看一下周边代码, 然后决定风格. 如果它们在所有的算术操作符两边都使用空格, 那么你也应该这样做. 如果它们的注释都用标记包围起来, 那么你的注释也要这样. 7 | 8 | 制定风格指南的目的在于让代码有规可循, 这样人们就可以专注于"你在说什么", 而不是"你在怎么说". 我们在这里给出的是全局的规范, 但是本地的规范同样重要. 如果你加到一个文件里的代码和原有代码大相径庭, 它会让读者不知所措. 避免这种情况. 9 | 10 | 11 | 12 | .. line-block:: 13 | 14 | Revision 2.60 15 | 16 | Amit Patel 17 | Antoine Picard 18 | Eugene Jhong 19 | Gregory P. Smith 20 | Jeremy Hylton 21 | Matt Smart 22 | Mike Shields 23 | Shane Liebling 24 | -------------------------------------------------------------------------------- /google-shell-styleguide/shell_files_and_interpreter_invocation.rst: -------------------------------------------------------------------------------- 1 | Shell文件和解释器调用 2 | =============================== 3 | 4 | 文件扩展名 5 | -------------------- 6 | 7 | .. tip:: 8 | 可执行文件应该没有扩展名(强烈建议)或者使用.sh扩展名。库文件必须使用.sh作为扩展名,而且应该是不可执行的。 9 | 10 | 当执行一个程序时,并不需要知道它是用什么语言编写的。而且shell脚本也不要求有扩展名。所以我们更喜欢可执行文件没有扩展名。 11 | 12 | 然而,对于库文件,知道其用什么语言编写的是很重要的,有时候会需要使用不同语言编写的相似的库文件。使用.sh这样特定语言后缀作为扩展名,就使得用不同语言编写的具有相同功能的库文件可以采用一样的名称。 13 | 14 | 15 | SUID / SGID 16 | -------------------- 17 | 18 | .. tip:: 19 | SUID(Set User ID)和SGID(Set Group ID)在shell脚本中是被禁止的。 20 | 21 | shell存在太多的安全问题,以致于如果允许SUID/SGID会使得shell几乎不可能足够安全。虽然bash使得运行SUID非常困难,但在某些平台上仍然有可能运行,这就是为什么我们明确提出要禁止它。 22 | 23 | 如果你需要较高权限的访问请使用 ``sudo`` 。 24 | -------------------------------------------------------------------------------- /google-python-styleguide/index.rst: -------------------------------------------------------------------------------- 1 | 扉页 2 | ================================ 3 | 4 | :版本: 2.6 5 | 6 | :原作者: 7 | .. line-block:: 8 | 9 | Amit Patel 10 | Antoine Picard 11 | Eugene Jhong 12 | Jeremy Hylton 13 | Matt Smart 14 | Mike Shields 15 | 16 | :翻译: 17 | .. line-block:: 18 | 19 | `guoqiao `_ v2.19 20 | `xuxinkun `_ v2.59 21 | `captainfffsama `_ v2.6 22 | 23 | :项目主页: 24 | - `Google Style Guide `_ 25 | - `Google 开源项目风格指南 - 中文版 `_ 26 | 27 | -------------------------------------------------------------------------------- /google-html-css-styleguide/general_style_rules.rst: -------------------------------------------------------------------------------- 1 | 整体样式规则 2 | ================ 3 | 4 | 协议 5 | --------------- 6 | 7 | 嵌入式资源省略协议头。 8 | 9 | 省略图片、媒体文件、样式表以及脚本的URL协议头部分(http:、https:),不使用这两个协议的文件则不省略。 10 | 省略协议头,即让URL变成相对地址,可以避免协议混合及小文件重复下载。 11 | 12 | .. code-block:: html 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | .. code-block:: css 21 | 22 | /* 不推荐 */ 23 | .example { 24 | background: url(http://www.google.com/images/example); 25 | } 26 | 27 | /* 推荐 */ 28 | .example { 29 | background: url(//www.google.com/images/example); 30 | } 31 | 32 | -------------------------------------------------------------------------------- /google-objc-styleguide/patterns.rst: -------------------------------------------------------------------------------- 1 | Cocoa 模式 2 | ================ 3 | 4 | 委托模式 5 | ~~~~~~~~~~~~~ 6 | 7 | .. tip:: 8 | 9 | 委托对象不应该被 ``retain`` 10 | 11 | 实现委托模式的类应: 12 | 13 | #. 拥有一个名为 ``delegate_`` 的实例变量来引用委托。 14 | #. 因此,访问器方法应该命名为 ``delegate`` 和 ``setDelegate:``。 15 | #. ``delegate_`` 对象不应该被 ``retain``。 16 | 17 | 模型/视图/控制器(MVC) 18 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 | 20 | .. tip:: 21 | 22 | 分离模型与视图。分离控制器与视图、模型。回调 API 使用 ``@protocol``。 23 | 24 | * 分离模型与视图:不要假设模型或者数据源的表示方法。保持数据源与表示层之间的接口抽象。视图不需要了解模型的逻辑(主要的规则是问问你自己,对于数据源的一个实例,有没有可能有多种不同状态的表示方法)。 25 | 26 | * 分离控制器与模型、视图:不要把所有的 “业务逻辑” 放进跟视图有关的类中。这使代码非常难以复用。使用控制器类来处理这些代码,但保证控制器不需要了解太多表示层的逻辑。 27 | 28 | * 使用 ``@protocol`` 来定义回调 API,如果不是所有的方法都必须实现,使用 ``@optional``(特例:使用 Objective-C 1.0 时,``@optional`` 不可用,可使用类别来定义一个 “非正规的协议”)。 29 | 30 | 31 | -------------------------------------------------------------------------------- /google-shell-styleguide/background.rst: -------------------------------------------------------------------------------- 1 | 背景 2 | =============================== 3 | 4 | 使用哪一种Shell 5 | -------------------- 6 | 7 | .. tip:: 8 | Bash是唯一被允许执行的shell脚本语言。 9 | 10 | 可执行文件必须以 ``#!/bin/bash`` 和最小数量的标志开始。请使用 ``set`` 来设置shell的选项,使得用 ``bash `` 调用你的脚本时不会破坏其功能。 11 | 12 | 限制所有的可执行shell脚本为bash使得我们安装在所有计算机中的shell语言保持一致性。 13 | 14 | 无论你是为什么而编码,对此唯一例外的是当你被迫时可以不这么做的。其中一个例子是Solaris SVR4包,编写任何脚本都需要用纯Bourne shell。 15 | 16 | 17 | 什么时候使用Shell 18 | -------------------- 19 | 20 | .. tip:: 21 | Shell应该仅仅被用于小功能或者简单的包装脚本。 22 | 23 | 尽管Shell脚本不是一种开发语言,但在整个谷歌它被用于编写多种实用工具的脚本。这个风格指南更多的是认同它的使用,而不是一个建议,即它可被用于广泛部署。 24 | 25 | 以下是一些准则: 26 | 27 | * 如果你主要是在调用其他的工具并且做一些相对很小数据量的操作,那么使用shell来完成任务是一种可接受的选择。 28 | * 如果你在乎性能,那么请选择其他工具,而不是使用shell。 29 | * 如果你发现你需要使用数据而不是变量赋值(如 ``${PHPESTATUS}`` ),那么你应该使用Python脚本。 30 | * 如果你将要编写的脚本会超过100行,那么你可能应该使用Python来编写,而不是Shell。请记住,当脚本行数增加,尽早使用另外一种语言重写你的脚本,以避免之后花更多的时间来重写。 31 | -------------------------------------------------------------------------------- /google-html-css-styleguide/general_meta_rules.rst: -------------------------------------------------------------------------------- 1 | 整体的元数据规则 2 | =================== 3 | 4 | 编码 5 | --------- 6 | 7 | 使用UTF-8无BOM编码。 8 | 9 | 让你的编辑器使用无字节顺序标记的UTF-8编码。 10 | 11 | 在HTML模板和文档中使用 ```` 指定编码。不需要为样式表指定编码,它默认是UTF-8。 12 | 13 | (想了解更多关于应该何时并如何指定编码,请查看 `Handling character encodings in HTML and CSS `_) 14 | 15 | 注释 16 | -------- 17 | 18 | 在需要时尽可能去解释你的代码。 19 | 20 | 用注释去解释你的代码,包括它的应用范围、用途、此方案的选择理由等。 21 | 22 | (这一条是可选的,没必要为每个文件写上详细的注释,会增重HTML/CSS的代码,主要取决于项目的复杂度。) 23 | 24 | 处理内容 25 | ---------- 26 | 27 | 用TODO标记待办事宜和处理内容。 28 | 29 | 只用TODO来标记待办事宜,不要使用其他格式,例如@@。 30 | 31 | 在括号里添加联系方式(姓名或邮箱),格式为TODO(联系方式)。 32 | 33 | 在冒号后面添加处理内容,格式为TODO:处理内容。 34 | 35 | .. code-block:: html 36 | 37 | {# TODO(john.doe): 重新处理水平居中 #} 38 |
Test
39 | 40 | 41 |
    42 |
  • Apples
  • 43 |
  • Oranges
  • 44 |
45 | -------------------------------------------------------------------------------- /google-html-css-styleguide/general_formatting_rules.rst: -------------------------------------------------------------------------------- 1 | 总体排版规则 2 | ============== 3 | 4 | 缩进 5 | --------- 6 | 7 | 每次缩进使用两个空格。 8 | 9 | 不使用TAB键或者混合使用TAB键和空格进行缩进。 10 | 11 | .. code-block:: html 12 | 13 |
    14 |
  • Fantastic 15 |
  • Great 16 |
17 | 18 | .. code-block:: css 19 | 20 | .example { 21 | color: blue; 22 | } 23 | 24 | 大小写 25 | ---------- 26 | 27 | 只使用小写字母。 28 | 29 | 所有的代码都使用小写字母:适用于HTML元素、属性、属性值(除了text/CDATA)、CSS选择器、属性名以及属性值(字符串除外)。 30 | 31 | .. code-block:: html 32 | 33 | 34 | Home 35 | 36 | 37 | Google 38 | 39 | .. code-block:: css 40 | 41 | /* 不推荐 */ 42 | color: #E5E5E5; 43 | 44 | /* 推荐 */ 45 | color: #e5e5e5; 46 | 47 | 尾部的空格 48 | ------------ 49 | 50 | 删除尾部的空格。 51 | 52 | 尾部的空格是多余的,不删除则形成无意义的文件差异。 53 | 54 | .. code-block:: html 55 | 56 | 57 |

What?_ 58 | 59 | 60 |

Yes please. 61 | -------------------------------------------------------------------------------- /manually-generate-pdf-document.md: -------------------------------------------------------------------------------- 1 | Manually generate PDF documents 2 | ------- 3 | 4 | # Debian 9 5 | 6 | ```shell 7 | sudo apt install texlive-lang-chinese texlive-lang-cjk texlive-xetex \ 8 | latexmk texlive-latex-extra texlive-latex-recommended python-pip \ 9 | texlive-latex-base texlive-plain-extra texlive-fonts-recommended \ 10 | texlive-generic-recommended 11 | ``` 12 | 13 | # Python pip 14 | 15 | ```shell 16 | sudo pip install Sphinx commonmark sphinx-rtd-theme 17 | ``` 18 | 19 | if download is too slow, try use local mirror 20 | 21 | ```shell 22 | sudo pip Sphinx commonmark sphinx-rtd-theme -i https://mirrors.aliyun.com/pypi/simple/ 23 | ``` 24 | 25 | # Generate PDF 26 | 27 | ```shell 28 | make latex 29 | make latexpdf 30 | ``` 31 | 32 | # Q & A 33 | 34 | 1. If occuring XXX file missing 35 | 36 | use follow url relace REPLACE-MISSING-FILE-NAME to you want find and install missing package 37 | 38 | https://packages.debian.org/search?searchon=contents&keywords=RELACE-MISSING-FILE-NAME&mode=path&suite=oldstable&arch=any 39 | 40 | 41 | -------------------------------------------------------------------------------- /google-html-css-styleguide/html_formatting_rules.rst: -------------------------------------------------------------------------------- 1 | HTML格式规则 2 | ============== 3 | 4 | 常规格式化 5 | ------------ 6 | 7 | 对每个块、列表、表格元素都另起一行,每个子元素都缩进。 8 | 9 | 每个块元素、列表元素或表格元素另起一行,而不必考虑元素的样式(因CSS可以改变元素的 ``display`` 属性)。 10 | 11 | 同样的,如果他们是块、列表或者表格元素的子元素,则将之缩进。 12 | 13 | (如果你遇到列表项之间有空白的问题,可以把所有 ``li`` 元素放到一行。Linter鼓励抛出警告而不是错误。) 14 | 15 | .. code-block:: html 16 | 17 |

18 |

Space, the final frontier.

19 |
20 | 21 |
    22 |
  • Moe 23 |
  • Larry 24 |
  • Curly 25 |
26 | 27 | 28 | 29 | 30 | 33 | 34 |
Income 31 | Taxes 32 |
$ 5.00 35 | $ 4.50 36 |
37 | 38 | HTML引号 39 | ----------- 40 | 41 | 当引用属性值时,使用双引号。 42 | 43 | 使用双引号而不是单引号来包裹属性值。 44 | 45 | .. code-block:: html 46 | 47 | 48 | Sign in 49 | 50 | 51 | Sign in 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # VS Code Config Files 2 | .vscode/ 3 | 4 | # JetBrains Webstorm Config Files 5 | .idea/ 6 | 7 | # Byte-compiled / optimized / DLL files 8 | __pycache__/ 9 | *.py[cod] 10 | *$py.class 11 | 12 | # C extensions 13 | *.so 14 | 15 | # Distribution / packaging 16 | .Python 17 | env/ 18 | build/ 19 | _build/ 20 | develop-eggs/ 21 | dist/ 22 | downloads/ 23 | eggs/ 24 | .eggs/ 25 | lib/ 26 | lib64/ 27 | parts/ 28 | sdist/ 29 | var/ 30 | *.egg-info/ 31 | .installed.cfg 32 | *.egg 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *,cover 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | 61 | # Sphinx documentation 62 | docs/_build/ 63 | 64 | # PyBuilder 65 | target/ 66 | 67 | # OS X 68 | .DS_Store 69 | -------------------------------------------------------------------------------- /google-typescript-styleguide/consistency.rst: -------------------------------------------------------------------------------- 1 | 一致性 2 | ################################################################################ 3 | 4 | 对于本文中并未明确解释的任何与代码风格有关的问题,都应当与同一文件中其它代码的现有写法 **保持一致** 。如果问题仍未得到解决,则应当参考同一文件夹下其它文件的写法。 5 | 6 | .. _ts-goals: 7 | 8 | 目标 9 | ******************************************************************************** 10 | 11 | 通常情况下,程序员自己是最了解他们的代码需求的人。所以,对于那些答案不唯一、而且最优解取决于实际场景的问题,一般应当由当事人根据情况自行决定解决方案。因此,对于这类问题,默认回答往往都是“不管了”。 12 | 13 | 以下几点则是其中的特例,它们解释了为什么要在这篇风格指南中编写全局性的规范。对于程序员自行规定的代码风格,应当根据以下几个原则对其进行评估: 14 | 15 | 1. **应当避免使用已知的会导致问题的代码范式,尤其是对于这门语言的新手而言** 16 | 17 | 例如: 18 | 19 | * ``any`` 是一个容易被误用的类型(某个变量 *真的* 可以既是一个数字,同时还可以作为函数被调用吗?),因此关于它的用法,指南中提出了一些建议。 20 | * TypeScript 的命名空间会为闭包优化带来问题。 21 | * 在文件名中使用句点 ``.`` 会让导入语句的样式变得不美观且令人困惑。 22 | * 类中的静态函数对优化十分不友好,同样的功能完全可以由文件级函数实现。 23 | * 不熟悉 ``private`` 关键字的用户会试图使用下划线将函数名变得混乱难懂。 24 | 25 | 2. **跨项目的代码应当保持一致的用法** 26 | 27 | 如果有两种语义上等价只是形式上不同的写法,应当只选择其中的一种,以避免代码中发生无意义的发散演化,同时也避免在代码审查的过程中进行无意义的争辩。 28 | 29 | 除此之外,还应当尽可能与 JavaScript 的代码风格保持一致,因为大部分程序员都会同时使用两种语言。 30 | 31 | 例如: 32 | 33 | * 变量名的首字母大小写风格。 34 | * ``x as T`` 语法和等价的 ``x`` 语法(后者不允许使用)。 35 | * ``Array<[number, number]>`` 和 ``[number, number][]`` 。 36 | 37 | 3. **代码应当具有长期可维护性** 38 | 39 | 代码的生命周期往往比其原始作者为其工作的时间要长,而 TypeScript 团队必须保证谷歌的所有工作在未来依然能顺利进行。 40 | 41 | 例如: 42 | 43 | * 使用自动化工具修改代码,所有的代码均经过自动格式化以符合空格样式的规范。 44 | * 规定了一组 Clousure 编译器标识,使 TS 代码库在编写过程中无需考虑编译选项的问题,也让用户能够安全地使用共享库。 45 | * 代码在使用其它库时必须进行导入(严格依赖),以便依赖项中的重构不会改变其用户的依赖项。 46 | * 用户必须编写测试。如果没有测试,就无法保证对语言或 google3 库中的改动不会破坏用户现有的代码。 47 | 48 | 4. **代码审查员应当着力于提高代码质量,而非强制推行各种规则** 49 | 50 | 如果能够将规范实现为自动化检查工具,这通常都是一个好的做法。这对上文中的第三条原则也有所帮助。 51 | 52 | 对于确实无关紧要的问题,例如语言中十分罕见的边界情况,或者避免了一个不太可能发生的 Bug ,等等,不妨直接无视之。 53 | -------------------------------------------------------------------------------- /google-typescript-styleguide/preface.rst: -------------------------------------------------------------------------------- 1 | 前言 2 | ################################################################################ 3 | 4 | .. _ts-introduction: 5 | 6 | 简介 7 | ******************************************************************************** 8 | 9 | 这份风格指南基于谷歌的内部版本,并在此基础上做了一些修改,使其具有更广泛的适用性。指南并非定期自动部署,而是由志愿者们根据需求进行维护与更新。 10 | 11 | 指南中的内容包括代码规范与最佳实践两部分。读者可根据所在团队的需求加以参考和选用。 12 | 13 | 指南中对 *必须、禁止、应当、不应、可以* 等词语的用法遵循 `RFC 2119 `_ 中的定义。文中的所有示例均非适合实际项目的正式用法,只用于对指南中的内容加以说明。 14 | 15 | .. _ts-about: 16 | 17 | 翻译信息 18 | ******************************************************************************** 19 | 20 | .. _ts-about-last-update: 21 | 22 | 上次更新日期 23 | ================================================================================ 24 | 25 | 2021 年 09 月 02 日。 26 | 27 | .. _ts-about-author: 28 | 29 | 作者 30 | ================================================================================ 31 | 32 | * `TinkerRobot `_ 33 | 34 | .. _ts-about-original: 35 | 36 | 原文链接 37 | ================================================================================ 38 | 39 | `Google TypeScript Style Guide `_ 40 | 41 | .. _ts-about-translation: 42 | 43 | 中文版链接 44 | ================================================================================ 45 | 46 | `谷歌 TypeScript 风格指南 `_ 47 | 48 | .. _ts-about-changelog: 49 | 50 | 修订历史 51 | ================================================================================ 52 | 53 | * **2021 年 09 月 02 日:** `TinkerRobot `_ 提交了第一个版本。 54 | -------------------------------------------------------------------------------- /google-shell-styleguide/comments.rst: -------------------------------------------------------------------------------- 1 | 注释 2 | ================================ 3 | 4 | 文件头 5 | -------------------- 6 | 7 | .. tip:: 8 | 每个文件的开头是其文件内容的描述。 9 | 10 | 每个文件必须包含一个顶层注释,对其内容进行简要概述。版权声明和作者信息是可选的。 11 | 12 | 例如: 13 | 14 | .. code-block:: shell 15 | 16 | #!/bin/bash 17 | # 18 | # Perform hot backups of Oracle databases. 19 | 20 | 功能注释 21 | -------------------- 22 | 23 | .. tip:: 24 | 任何不是既明显又短的函数都必须被注释。任何库函数无论其长短和复杂性都必须被注释。 25 | 26 | 其他人通过阅读注释(和帮助信息,如果有的话)就能够学会如何使用你的程序或库函数,而不需要阅读代码。 27 | 28 | 所有的函数注释应该包含: 29 | 30 | * 函数的描述 31 | * 全局变量的使用和修改 32 | * 使用的参数说明 33 | * 返回值,而不是上一条命令运行后默认的退出状态 34 | 35 | 例如: 36 | 37 | .. code-block:: shell 38 | 39 | #!/bin/bash 40 | # 41 | # Perform hot backups of Oracle databases. 42 | 43 | export PATH='/usr/xpg4/bin:/usr/bin:/opt/csw/bin:/opt/goog/bin' 44 | 45 | ####################################### 46 | # Cleanup files from the backup dir 47 | # Globals: 48 | # BACKUP_DIR 49 | # ORACLE_SID 50 | # Arguments: 51 | # None 52 | # Returns: 53 | # None 54 | ####################################### 55 | cleanup() { 56 | ... 57 | } 58 | 59 | 实现部分的注释 60 | -------------------- 61 | 62 | .. tip:: 63 | 注释你代码中含有技巧、不明显、有趣的或者重要的部分。 64 | 65 | 这部分遵循谷歌代码注释的通用做法。不要注释所有代码。如果有一个复杂的算法或者你正在做一些与众不同的,放一个简单的注释。 66 | 67 | TODO注释 68 | -------------------- 69 | 70 | .. tip:: 71 | 使用TODO注释临时的、短期解决方案的、或者足够好但不够完美的代码。 72 | 73 | 这与C++指南中的约定相一致。 74 | 75 | TODOs应该包含全部大写的字符串TODO,接着是括号中你的用户名。冒号是可选的。最好在TODO条目之后加上 bug或者ticket 的序号。 76 | 77 | 例如: 78 | 79 | .. code-block:: shell 80 | 81 | # TODO(mrmonkey): Handle the unlikely edge cases (bug ####) 82 | 83 | -------------------------------------------------------------------------------- /google-shell-styleguide/calling_commands.rst: -------------------------------------------------------------------------------- 1 | 调用命令 2 | ================================ 3 | 4 | 检查返回值 5 | -------------------- 6 | 7 | .. tip:: 8 | 总是检查返回值,并给出信息返回值。 9 | 10 | 对于非管道命令,使用 ``$?`` 或直接通过一个 ``if`` 语句来检查以保持其简洁。 11 | 12 | 例如: 13 | 14 | .. code-block:: shell 15 | 16 | if ! mv "${file_list}" "${dest_dir}/" ; then 17 | echo "Unable to move ${file_list} to ${dest_dir}" >&2 18 | exit "${E_BAD_MOVE}" 19 | fi 20 | 21 | # Or 22 | mv "${file_list}" "${dest_dir}/" 23 | if [[ "$?" -ne 0 ]]; then 24 | echo "Unable to move ${file_list} to ${dest_dir}" >&2 25 | exit "${E_BAD_MOVE}" 26 | fi 27 | 28 | Bash也有 ``PIPESTATUS`` 变量,允许检查从管道所有部分返回的代码。如果仅仅需要检查整个管道是成功还是失败,以下的方法是可以接受的: 29 | 30 | .. code-block:: shell 31 | 32 | tar -cf - ./* | ( cd "${dir}" && tar -xf - ) 33 | if [[ "${PIPESTATUS[0]}" -ne 0 || "${PIPESTATUS[1]}" -ne 0 ]]; then 34 | echo "Unable to tar files to ${dir}" >&2 35 | fi 36 | 37 | 可是,只要你运行任何其他命令, ``PIPESTATUS`` 将会被覆盖。如果你需要基于管道中发生的错误执行不同的操作,那么你需要在运行命令后立即将 ``PIPESTATUS`` 赋值给另一个变量(别忘了 ``[`` 是一个会将 ``PIPESTATUS`` 擦除的命令)。 38 | 39 | .. code-block:: shell 40 | 41 | tar -cf - ./* | ( cd "${DIR}" && tar -xf - ) 42 | return_codes=(${PIPESTATUS[*]}) 43 | if [[ "${return_codes[0]}" -ne 0 ]]; then 44 | do_something 45 | fi 46 | if [[ "${return_codes[1]}" -ne 0 ]]; then 47 | do_something_else 48 | fi 49 | 50 | 内建命令和外部命令 51 | -------------------- 52 | 53 | .. tip:: 54 | 可以在调用shell内建命令和调用另外的程序之间选择,请选择内建命令。 55 | 56 | 我们更喜欢使用内建命令,如在 ``bash(1)`` 中参数扩展函数。因为它更强健和便携(尤其是跟像 ``sed`` 这样的命令比较) 57 | 58 | 例如: 59 | 60 | .. code-block:: shell 61 | 62 | # Prefer this: 63 | addition=$((${X} + ${Y})) 64 | substitution="${string/#foo/bar}" 65 | 66 | # Instead of this: 67 | addition="$(expr ${X} + ${Y})" 68 | substitution="$(echo "${string}" | sed -e 's/^foo/bar/')" 69 | 70 | -------------------------------------------------------------------------------- /google-cpp-styleguide/exceptions.rst: -------------------------------------------------------------------------------- 1 | 10. 规则特例 2 | ------------------ 3 | 4 | 前面说明的编程习惯基本都是强制性的. 但所有优秀的规则都允许例外, 这里就是探讨这些特例. 5 | 6 | 10.1. 现有不合规范的代码 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | **总述** 10 | 11 | 对于现有不符合既定编程风格的代码可以网开一面. 12 | 13 | **说明** 14 | 15 | 当你修改使用其他风格的代码时, 为了与代码原有风格保持一致可以不使用本指南约定. 如果不放心, 可以与代码原作者或现在的负责人员商讨. 记住, *一致性* 也包括原有的一致性. 16 | 17 | .. _windows-code: 18 | 19 | 10.2. Windows 代码 20 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 21 | 22 | **总述** 23 | 24 | Windows 程序员有自己的编程习惯, 主要源于 Windows 头文件和其它 Microsoft 代码. 我们希望任何人都可以顺利读懂你的代码, 所以针对所有平台的 C++ 编程只给出一个单独的指南. 25 | 26 | **说明** 27 | 28 | 如果你习惯使用 Windows 编码风格, 这儿有必要重申一下某些你可能会忘记的指南: 29 | 30 | - 不要使用匈牙利命名法 (比如把整型变量命名成 ``iNum``). 使用 Google 命名约定, 包括对源文件使用 ``.cc`` 扩展名. 31 | 32 | - Windows 定义了很多原生类型的同义词 (YuleFox 注: 这一点, 我也很反感), 如 ``DWORD``, ``HANDLE`` 等等. 在调用 Windows API 时这是完全可以接受甚至鼓励的. 即使如此, 还是尽量使用原有的 C++ 类型, 例如使用 ``const TCHAR *`` 而不是 ``LPCTSTR``. 33 | 34 | - 使用 Microsoft Visual C++ 进行编译时, 将警告级别设置为 3 或更高, 并将所有警告(warnings)当作错误(errors)处理. 35 | 36 | - 不要使用 ``#pragma once``; 而应该使用 Google 的头文件保护规则. 头文件保护的路径应该相对于项目根目录 (Yang.Y 注: 如 ``#ifndef SRC_DIR_BAR_H_``, 参考 :ref:`#define 保护 ` 一节). 37 | 38 | - 除非万不得已, 不要使用任何非标准的扩展, 如 ``#pragma`` 和 ``__declspec``. 使用 ``__declspec(dllimport)`` 和 ``__declspec(dllexport)`` 是允许的, 但必须通过宏来使用, 比如 ``DLLIMPORT`` 和 ``DLLEXPORT``, 这样其他人在分享使用这些代码时可以很容易地禁用这些扩展. 39 | 40 | 然而, 在 Windows 上仍然有一些我们偶尔需要违反的规则: 41 | 42 | - 通常我们 :ref:`禁止使用多重继承 `, 但在使用 COM 和 ATL/WTL 类时可以使用多重继承. 为了实现 COM 或 ATL/WTL 类/接口, 你可能不得不使用多重实现继承. 43 | 44 | - 虽然代码中不应该使用异常, 但是在 ATL 和部分 STL(包括 Visual C++ 的 STL) 中异常被广泛使用. 使用 ATL 时, 应定义 ``_ATL_NO_EXCEPTIONS`` 以禁用异常. 你需要研究一下是否能够禁用 STL 的异常, 如果无法禁用, 可以启用编译器异常. (注意这只是为了编译 STL, 自己的代码里仍然不应当包含异常处理). 45 | 46 | - 通常为了利用头文件预编译, 每个每个源文件的开头都会包含一个名为 ``StdAfx.h`` 或 ``precompile.h`` 的文件. 为了使代码方便与其他项目共享, 请避免显式包含此文件 (除了在 ``precompile.cc`` 中), 使用 ``/FI`` 编译器选项以自动包含该文件. 47 | 48 | - 资源头文件通常命名为 ``resource.h`` 且只包含宏, 这一文件不需要遵守本风格指南. 49 | 50 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Google 开源项目风格指南——中文版 2 | ================================ 3 | 4 | * ReadTheDocs 托管地址: `在线阅读最新版本 `_ 5 | 6 | * GitHub 托管地址: `zh-google-styleguide `_ 7 | 8 | * 离线文档下载地址: `release `_ 9 | 10 | .. note:: 11 | 12 | **声明** 13 | 14 | 本项目并非 Google 官方项目,而是由国内程序员凭热情创建和维护。 15 | 16 | 如果你关注的是 Google 官方英文版,请移步 `Google Style Guide `_ 。 17 | 18 | 每个较大的开源项目都有自己的风格指南:关于如何为该项目编写代码的一系列约定(有时候会比较武断)。当所有代码均保持一致的风格,在理解大型代码库时更为轻松。 19 | 20 | “风格”的含义涵盖范围广,从“变量使用驼峰格式(camelCase)”到“决不使用全局变量”再到“决不使用异常”,等等诸如此类。 21 | 22 | 英文版项目维护的是在 Google 使用的编程风格指南。如果你正在修改的项目源自 Google,你可能会被引导至英文版项目页面,以了解项目所使用的风格。 23 | 24 | 我们已经发布了七份 **中文版** 的风格指南: 25 | 26 | #. `Google C++ 风格指南 `_ 27 | 28 | #. `Google Objective-C 风格指南 `_ 29 | 30 | #. `Google Python 风格指南 `_ 31 | 32 | #. `Google JavaScript 风格指南 `_ 33 | 34 | #. `Google Shell 风格指南 `_ 35 | 36 | #. `Google JSON 风格指南 `_ 37 | 38 | #. `Google TypeScript 风格指南 `_ 39 | 40 | 中文版项目采用 reStructuredText 纯文本标记语法,并使用 Sphinx 生成 HTML / CHM / PDF 等文档格式。 41 | 42 | * 英文版项目还包含 `cpplint `_ ——一个用来帮助适应风格准则的工具,以及 `google-c-style.el `_,Google 风格的 Emacs 配置文件。 43 | 44 | * 另外,招募志愿者翻译 `JavaScript Style Guide `_ 以及 `XML Document Format Style Guide `_ ,有意者请联系 `Yang.Y `_ 。 45 | -------------------------------------------------------------------------------- /conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | 5 | import sys, os 6 | project = u'Google 开源项目风格指南' 7 | copyright = u'' 8 | version = u'' 9 | release = u'' 10 | 11 | source_suffix = '.rst' 12 | master_doc = 'contents' 13 | language = 'en_US' 14 | exclude_patterns = ['_build'] 15 | extensions = ['sphinx.ext.imgmath'] 16 | pygments_style = 'sphinx' 17 | 18 | # on_rtd is whether we are on readthedocs.org 19 | import os 20 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 21 | 22 | if not on_rtd: # only import and set the theme if we're building docs locally 23 | import sphinx_rtd_theme 24 | html_theme = 'sphinx_rtd_theme' 25 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] 26 | 27 | # otherwise, readthedocs.org uses their theme by default, so no need to specify it 28 | 29 | html_title = u'Google 开源项目风格指南' 30 | htmlhelp_basename = 'zh-google-styleguide' 31 | html_add_permalinks = '' 32 | 33 | latex_engine = 'xelatex' 34 | file_insertion_enabled = False 35 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 36 | if on_rtd: 37 | latex_elements = { 38 | # The paper size ('letterpaper' or 'a4paper'). 39 | #'papersize': 'letterpaper', 40 | # The font size ('10pt', '11pt' or '12pt'). 41 | #'pointsize': '10pt', 42 | # Additional stuff for the LaTeX preamble. 43 | 'preamble': r''' 44 | \hypersetup{unicode=true} 45 | \usepackage{CJKutf8} 46 | \DeclareUnicodeCharacter{00A0}{\nobreakspace} 47 | \DeclareUnicodeCharacter{2203}{\ensuremath{\exists}} 48 | \DeclareUnicodeCharacter{2200}{\ensuremath{\forall}} 49 | \DeclareUnicodeCharacter{2286}{\ensuremath{\subseteq}} 50 | \DeclareUnicodeCharacter{2713}{x} 51 | \DeclareUnicodeCharacter{27FA}{\ensuremath{\Longleftrightarrow}} 52 | \DeclareUnicodeCharacter{221A}{\ensuremath{\sqrt{}}} 53 | \DeclareUnicodeCharacter{221B}{\ensuremath{\sqrt[3]{}}} 54 | \DeclareUnicodeCharacter{2295}{\ensuremath{\oplus}} 55 | \DeclareUnicodeCharacter{2297}{\ensuremath{\otimes}} 56 | \begin{CJK}{UTF8}{gbsn} 57 | \AtEndDocument{\end{CJK}} ''', 58 | } 59 | else: 60 | latex_elements = { 61 | 'papersize' : 'a4paper', 62 | 'utf8extra' : '', 63 | 'inputenc' : '', 64 | 'babel' : r'''\usepackage[english]{babel}''', 65 | 'preamble' : r''' \usepackage{ctex} ''', 66 | } 67 | latex_documents = [ 68 | ('contents', 'zh-google-styleguide.tex', u'Google 开源项目风格指南', 69 | u'', 'manual'), 70 | ] 71 | 72 | 73 | #Add sponsorship and project information to the template context. 74 | context = { 75 | 'MEDIA_URL': "/media/", 76 | 'slug': 'google-styleguide', 77 | 'name': u'Google 开源项目风格指南', 78 | 'analytics_code': 'None', 79 | } 80 | 81 | html_context = context 82 | -------------------------------------------------------------------------------- /google-cpp-styleguide/index.rst: -------------------------------------------------------------------------------- 1 | 0. 扉页 2 | ============ 3 | 4 | :版本: 4.45 5 | 6 | :原作者: 7 | 8 | .. line-block:: 9 | 10 | Benjy Weinberger 11 | Craig Silverstein 12 | Gregory Eitzmann 13 | Mark Mentovai 14 | Tashana Landray 15 | 16 | :翻译: 17 | 18 | .. line-block:: 19 | 20 | `YuleFox `_ 21 | `Yang.Y `_ 22 | `acgtyrant `_ 23 | `lilinsanity `_ 24 | 25 | :项目主页: 26 | 27 | - `Google Style Guide `_ 28 | - `Google 开源项目风格指南 - 中文版 `_ 29 | 30 | 0.1 译者前言 31 | -------------------- 32 | 33 | Google 经常会发布一些开源项目, 意味着会接受来自其他代码贡献者的代码. 但是如果代码贡献者的编程风格与 Google 的不一致, 会给代码阅读者和其他代码提交者造成不小的困扰. Google 因此发布了这份自己的编程风格指南, 使所有提交代码的人都能获知 Google 的编程风格. 34 | 35 | 翻译初衷: 36 | 37 | 规则的作用就是避免混乱. 但规则本身一定要权威, 有说服力, 并且是理性的. 我们所见过的大部分编程规范, 其内容或不够严谨, 或阐述过于简单, 或带有一定的武断性. 38 | 39 | Google 保持其一贯的严谨精神, 5 万汉字的指南涉及广泛, 论证严密. 我们翻译该系列指南的主因也正是其严谨性. 严谨意味着指南的价值不仅仅局限于它罗列出的规范, 更具参考意义的是它为了列出规范而做的谨慎权衡过程. 40 | 41 | 指南不仅列出你要怎么做, 还告诉你为什么要这么做, 哪些情况下可以不这么做, 以及如何权衡其利弊. 其他团队未必要完全遵照指南亦步亦趋, 如前面所说, 这份指南是 Google 根据自身实际情况打造的, 适用于其主导的开源项目. 其他团队可以参照该指南, 或从中汲取灵感, 建立适合自身实际情况的规范. 42 | 43 | 我们在翻译的过程中, 收获颇多. 希望本系列指南中文版对你同样能有所帮助. 44 | 45 | 我们翻译时也是尽力保持严谨, 但水平所限, bug 在所难免. 有任何意见或建议, 可与我们取得联系. 46 | 47 | 中文版和英文版一样, 使用 ``Artistic License/GPL`` 开源许可. 48 | 49 | 中文版修订历史: 50 | 51 | - 2015-08 : 热心的清华大学同学 @lilinsanity 完善了「类」章节以及其它一些小章节。至此,对 Google CPP Style Guide 4.45 的翻译正式竣工。 52 | 53 | - 2015-07 4.45 : acgtyrant 为了学习 C++ 的规范,顺便重新翻译了本 C++ 风格指南,特别是 C++11 的全新内容。排版大幅度优化,翻译措辞更地道,添加了新译者笔记。Google 总部 C++ 工程师 innocentim, 清华大学不愿意透露姓名的唐马儒先生,大阪大学大学院情报科学研究科计算机科学专攻博士 farseerfc 和其它 Arch Linux 中文社区众帮了译者不少忙,谢谢他们。因为 C++ Primer 尚未完全入门,暂时没有翻译「类」章节和其它一些小章节。 54 | 55 | - 2009-06 3.133 : YuleFox 的 1.0 版已经相当完善, 但原版在近一年的时间里, 其规范也发生了一些变化. 56 | 57 | Yang.Y 与 YuleFox 一拍即合, 以项目的形式来延续中文版 : `Google 开源项目风格指南 - 中文版项目 `_. 58 | 59 | 主要变化是同步到 3.133 最新英文版本, 做部分勘误和改善可读性方面的修改, 并改进排版效果. Yang.Y 重新翻修, YuleFox 做后续评审. 60 | 61 | - 2008-07 1.0 : 出自 `YuleFox 的 Blog `_, 很多地方摘录的也是该版本. 62 | 63 | 64 | 0.2 背景 65 | -------------- 66 | 67 | C++ 是 Google 大部分开源项目的主要编程语言. 正如每个 C++ 程序员都知道的, C++ 有很多强大的特性, 但这种强大不可避免的导致它走向复杂,使代码更容易产生 bug, 难以阅读和维护. 68 | 69 | 本指南的目的是通过详细阐述 C++ 注意事项来驾驭其复杂性. 这些规则在保证代码易于管理的同时, 也能高效使用 C++ 的语言特性. 70 | 71 | *风格*, 亦被称作可读性, 也就是指导 C++ 编程的约定. 使用术语 "风格" 有些用词不当, 因为这些习惯远不止源代码文件格式化这么简单. 72 | 73 | 使代码易于管理的方法之一是加强代码一致性. 让任何程序员都可以快速读懂你的代码这点非常重要. 保持统一编程风格并遵守约定意味着可以很容易根据 "模式匹配" 规则来推断各种标识符的含义. 创建通用, 必需的习惯用语和模式可以使代码更容易理解. 在一些情况下可能有充分的理由改变某些编程风格, 但我们还是应该遵循一致性原则,尽量不这么做. 74 | 75 | 本指南的另一个观点是 C++ 特性的臃肿. C++ 是一门包含大量高级特性的庞大语言. 某些情况下, 我们会限制甚至禁止使用某些特性. 这么做是为了保持代码清爽, 避免这些特性可能导致的各种问题. 指南中列举了这类特性, 并解释为什么这些特性被限制使用. 76 | 77 | Google 主导的开源项目均符合本指南的规定. 78 | 79 | 注意: 本指南并非 C++ 教程, 我们假定读者已经对 C++ 非常熟悉. 80 | -------------------------------------------------------------------------------- /google-cpp-styleguide/magic.rst: -------------------------------------------------------------------------------- 1 | 5. 来自 Google 的奇技 2 | -------------------------------------- 3 | 4 | Google 用了很多自己实现的技巧 / 工具使 C++ 代码更加健壮, 我们使用 C++ 的方式可能和你在其它地方见到的有所不同. 5 | 6 | 5.1. 所有权与智能指针 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | **> 总述** 10 | 11 | 动态分配出的对象最好有单一且固定的所有主, 并通过智能指针传递所有权. 12 | 13 | **> 定义** 14 | 15 | 所有权是一种登记/管理动态内存和其它资源的技术. 动态分配对象的所有主是一个对象或函数, 后者负责确保当前者无用时就自动销毁前者. 所有权有时可以共享, 此时就由最后一个所有主来负责销毁它. 甚至也可以不用共享, 在代码中直接把所有权传递给其它对象. 16 | 17 | 智能指针是一个通过重载 ``*`` 和 ``->`` 运算符以表现得如指针一样的类. 智能指针类型被用来自动化所有权的登记工作, 来确保执行销毁义务到位. `std::unique_ptr `_ 是 C++11 新推出的一种智能指针类型, 用来表示动态分配出的对象的独一无二的所有权; 当 ``std::unique_ptr`` 离开作用域时, 对象就会被销毁. ``std::unique_ptr`` 不能被复制, 但可以把它移动(move)给新所有主. `std::shared_ptr `_ 同样表示动态分配对象的所有权, 但可以被共享, 也可以被复制; 对象的所有权由所有复制者共同拥有, 最后一个复制者被销毁时, 对象也会随着被销毁. 18 | 19 | **> 优点** 20 | 21 | * 如果没有清晰、逻辑条理的所有权安排, 不可能管理好动态分配的内存. 22 | 23 | * 传递对象的所有权, 开销比复制来得小, 如果可以复制的话. 24 | 25 | * 传递所有权也比"借用"指针或引用来得简单, 毕竟它大大省去了两个用户一起协调对象生命周期的工作. 26 | 27 | * 如果所有权逻辑条理, 有文档且不紊乱的话, 可读性会有很大提升. 28 | 29 | * 可以不用手动完成所有权的登记工作, 大大简化了代码, 也免去了一大波错误之恼. 30 | 31 | * 对于 const 对象来说, 智能指针简单易用, 也比深度复制高效. 32 | 33 | **> 缺点** 34 | 35 | * 不得不用指针(不管是智能的还是原生的)来表示和传递所有权. 指针语义可要比值语义复杂得许多了, 特别是在 API 里:这时不光要操心所有权, 还要顾及别名, 生命周期, 可变性以及其它大大小小的问题. 36 | 37 | * 其实值语义的开销经常被高估, 所以所有权传递带来的性能提升不一定能弥补可读性和复杂度的损失. 38 | 39 | * 如果 API 依赖所有权的传递, 就会害得客户端不得不用单一的内存管理模型. 40 | 41 | * 如果使用智能指针, 那么资源释放发生的位置就会变得不那么明显. 42 | 43 | * ``std::unique_ptr`` 的所有权传递原理是 C++11 的 move 语法, 后者毕竟是刚刚推出的, 容易迷惑程序员. 44 | 45 | * 如果原本的所有权设计已经够完善了, 那么若要引入所有权共享机制, 可能不得不重构整个系统. 46 | 47 | * 所有权共享机制的登记工作在运行时进行, 开销可能相当大. 48 | 49 | * 某些极端情况下 (例如循环引用), 所有权被共享的对象永远不会被销毁. 50 | 51 | * 智能指针并不能够完全代替原生指针. 52 | 53 | **> 结论** 54 | 55 | 如果必须使用动态分配, 那么更倾向于将所有权保持在分配者手中. 如果其他地方要使用这个对象, 最好传递它的拷贝, 或者传递一个不用改变所有权的指针或引用. 倾向于使用 ``std::unique_ptr`` 来明确所有权传递, 例如: 56 | 57 | .. code-block:: c++ 58 | 59 | std::unique_ptr FooFactory(); 60 | void FooConsumer(std::unique_ptr ptr); 61 | 62 | 如果没有很好的理由, 则不要使用共享所有权. 这里的理由可以是为了避免开销昂贵的拷贝操作, 但是只有当性能提升非常明显, 并且操作的对象是不可变的(比如说 ``std::shared_ptr`` )时候, 才能这么做. 如果确实要使用共享所有权, 建议于使用 ``std::shared_ptr`` . 63 | 64 | 不要使用 ``std::auto_ptr``, 使用 ``std::unique_ptr`` 代替它. 65 | 66 | 5.2. Cpplint 67 | ~~~~~~~~~~~~~~~~~~~~~~~~ 68 | 69 | **> 总述** 70 | 71 | 使用 ``cpplint.py`` 检查风格错误. 72 | 73 | **> 说明** 74 | 75 | ``cpplint.py`` 是一个用来分析源文件, 能检查出多种风格错误的工具. 它不并完美, 甚至还会漏报和误报, 但它仍然是一个非常有用的工具. 在行尾加 ``// NOLINT``, 或在上一行加 ``// NOLINTNEXTLINE``, 可以忽略报错. 76 | 77 | 某些项目会指导你如何使用他们的项目工具运行 ``cpplint.py``. 如果你参与的项目没有提供, 你可以单独下载 `cpplint.py `_. 78 | 79 | 80 | 译者(acgtyrant)笔记 81 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 82 | 83 | #. 把智能指针当成对象来看待的话, 就很好领会它与所指对象之间的关系了. 84 | #. 原来 Rust 的 Ownership 思想是受到了 C++ 智能指针的很大启发啊. 85 | #. ``scoped_ptr`` 和 ``auto_ptr`` 已过时. 现在是 ``shared_ptr`` 和 ``uniqued_ptr`` 的天下了. 86 | #. 按本文来说, 似乎除了智能指针, 还有其它所有权机制, 值得留意. 87 | #. Arch Linux 用户注意了, AUR 有对 cpplint 打包. 88 | -------------------------------------------------------------------------------- /google-objc-styleguide/comments.rst: -------------------------------------------------------------------------------- 1 | 注释 2 | ======= 3 | 4 | 虽然写起来很痛苦,但注释是保证代码可读性的关键。下面的规则给出了你应该什么时候、在哪进行注释。记住:尽管注释很重要,但最好的代码应该自成文档。与其给类型及变量起一个晦涩难懂的名字,再为它写注释,不如直接起一个有意义的名字。 5 | 6 | 当你写注释的时候,记得你是在给你的听众写,即下一个需要阅读你所写代码的贡献者。大方一点,下一个读代码的人可能就是你! 7 | 8 | 记住所有 C++ 风格指南里的规则在这里也同样适用,不同的之处后续会逐步指出。 9 | 10 | 文件注释 11 | ~~~~~~~~~~ 12 | 13 | .. tip:: 14 | 15 | 每个文件的开头以文件内容的简要描述起始,紧接着是作者,最后是版权声明和/或许可证样板。 16 | 17 | 版权信息及作者 18 | ^^^^^^^^^^^^^^^^^^ 19 | 20 | 每个文件应该按顺序包括如下项: 21 | 22 | * 文件内容的简要描述 23 | * 代码作者 24 | * 版权信息声明(如:``Copyright 2008 Google Inc.``) 25 | * 必要的话,加上许可证样板。为项目选择一个合适的授权样板(例如,``Apache 2.0, BSD, LGPL, GPL``)。 26 | 27 | 如果你对其他人的原始代码作出重大的修改,请把你自己的名字添加到作者里面。当另外一个代码贡献者对文件有问题时,他需要知道怎么联系你,这十分有用。 28 | 29 | 声明部分的注释 30 | ~~~~~~~~~~~~~~~~ 31 | 32 | .. tip:: 33 | 34 | 每个接口、类别以及协议应辅以注释,以描述它的目的及与整个项目的关系。 35 | 36 | .. code-block:: objc 37 | 38 | // A delegate for NSApplication to handle notifications about app 39 | // launch and shutdown. Owned by the main app controller. 40 | @interface MyAppDelegate : NSObject { 41 | ... 42 | } 43 | @end 44 | 45 | 如果你已经在文件头部详细描述了接口,可以直接说明 “完整的描述请参见文件头部”,但是一定要有这部分注释。 46 | 47 | 另外,公共接口的每个方法,都应该有注释来解释它的作用、参数、返回值以及其它影响。 48 | 49 | 为类的线程安全性作注释,如果有的话。如果类的实例可以被多个线程访问,记得注释多线程条件下的使用规则。 50 | 51 | 实现部分的注释 52 | ~~~~~~~~~~~~~~~~ 53 | 54 | .. tip:: 55 | 56 | 使用 ``|`` 来引用注释中的变量名及符号名而不是使用引号。 57 | 58 | 这会避免二义性,尤其是当符号是一个常用词汇,这使用语句读起来很糟糕。例如,对于符号 ``count`` : 59 | 60 | .. code-block:: objc 61 | 62 | // Sometimes we need |count| to be less than zero. 63 | 64 | 或者当引用已经包含引号的符号: 65 | 66 | .. code-block:: objc 67 | 68 | // Remember to call |StringWithoutSpaces("foo bar baz")| 69 | 70 | 71 | 72 | 对象所有权 73 | ~~~~~~~~~~~~~~~~ 74 | 75 | .. tip:: 76 | 77 | 当与 Objective-C 最常规的作法不同时,尽量使指针的所有权模型尽量明确。 78 | 79 | 继承自 ``NSObject`` 的对象的实例变量指针,通常被假定是强引用关系(retained),某些情况下也可以注释为弱引用(weak)或使用 ``__weak`` 生命周期限定符。同样,声明的属性如果没有被类 ``retained``,必须指定是弱引用或赋予 ``@property`` 属性。然而,Mac 软件中标记上 ``IBOutlets`` 的实例变量,被认为是不会被类 ``retained`` 的。 80 | 81 | 当实例变量指向 ``CoreFoundation``、C++ 或者其它非 Objective-C 对象时,不论指针是否会被 ``retained``,都需要使用 ``__strong`` 和 ``__weak`` 类型修饰符明确指明。``CoreFoundation`` 和其它非 Objective-C 对象指针需要显式的内存管理,即便使用了自动引用计数或垃圾回收机制。当不允许使用 ``__weak`` 类型修饰符(比如,使用 clang 编译时的 C++ 成员变量),应使用注释替代说明。 82 | 83 | 注意:Objective-C 对象中的 C++ 对象的自动封装,缺省是不允许的,参见 `这里 `_ 的说明。 84 | 85 | 强引用及弱引用声明的例子: 86 | 87 | .. code-block:: objc 88 | 89 | @interface MyDelegate : NSObject { 90 | @private 91 | IBOutlet NSButton *okButton_; // normal NSControl; implicitly weak on Mac only 92 | 93 | AnObjcObject* doohickey_; // my doohickey 94 | __weak MyObjcParent *parent_; // so we can send msgs back (owns me) 95 | 96 | // non-NSObject pointers... 97 | __strong CWackyCPPClass *wacky_; // some cross-platform object 98 | __strong CFDictionaryRef *dict_; 99 | } 100 | @property(strong, nonatomic) NSString *doohickey; 101 | @property(weak, nonatomic) NSString *parent; 102 | @end 103 | 104 | (译注:强引用 - 对象被类 ``retained``。弱引用 - 对象没有被类 ``retained``,如委托) 105 | -------------------------------------------------------------------------------- /google-html-css-styleguide/css_formatting_rules.rst: -------------------------------------------------------------------------------- 1 | CSS格式化规则 2 | =============== 3 | 4 | 声明顺序 5 | --------- 6 | 7 | 按字母顺序排列声明。 8 | 9 | css文件书写按字母顺序排列的方式,容易记忆和维护,以达到一致的代码。 10 | 11 | 在排序时忽略浏览器特定的前缀。但是,特定CSS属性的多个浏览器前缀应按字母顺序排列(如-moz书写在-webkit前面)。 12 | 13 | .. code-block:: css 14 | 15 | background: fuchsia; 16 | border: 1px solid; 17 | -moz-border-radius: 4px; 18 | -webkit-border-radius: 4px; 19 | border-radius: 4px; 20 | color: black; 21 | text-align: center; 22 | text-indent: 2em; 23 | 24 | 块内容的缩进 25 | -------------- 26 | 27 | 缩进块内容。 28 | 29 | 将包括嵌套及声明的 `块内容 `_ 进行缩进,以体现层次并提高可读性。 30 | 31 | .. code-block:: css 32 | 33 | @media screen, projection { 34 | 35 | html { 36 | background: #fff; 37 | color: #444; 38 | } 39 | 40 | } 41 | 42 | 声明结束 43 | ---------- 44 | 45 | 每个属性后使用分号结束。 46 | 47 | 以分号结束每个属性,提高一致性和可扩展性。 48 | 49 | .. code-block:: css 50 | 51 | /* 不推荐 */ 52 | .test { 53 | display: block; 54 | height: 100px 55 | } 56 | 57 | /* 推荐 */ 58 | .test { 59 | display: block; 60 | height: 100px; 61 | } 62 | 63 | CSS属性名结束 64 | --------------- 65 | 66 | 属性名称的冒号后有一个空格。 67 | 68 | 为保证一致性,在属性名与属性值之间添加一个空格(但是属性名和冒号间没有空格)。 69 | 70 | .. code-block:: css 71 | 72 | /* 不推荐 */ 73 | h3 { 74 | font-weight:bold; 75 | } 76 | 77 | /* 推荐 */ 78 | h3 { 79 | font-weight: bold; 80 | } 81 | 82 | 声明块间隔 83 | -------------- 84 | 85 | 在选择器和后面的声明块之间使用一个空格。 86 | 87 | 最后一个选择器与表示 `声名块 `_ 开始的左大花括号在同行,中间有一个字符空格。 88 | 89 | 表示开始的左大花括号和选择器在同行。 90 | 91 | .. code-block:: css 92 | 93 | /* 不推荐:缺少空间 */ 94 | #video{ 95 | margin-top: 1em; 96 | } 97 | 98 | 99 | /* 不推荐:不必要的换行符 */ 100 | #video 101 | { 102 | margin-top: 1em; 103 | } 104 | 105 | /* 推荐 */ 106 | #video { 107 | margin-top: 1em; 108 | } 109 | 110 | 111 | 选择器及声明分离 112 | ------------------- 113 | 114 | 每个选择器和声明独立成行。 115 | 116 | 总是让每个选择器和声明单独成行。 117 | 118 | .. code-block:: css 119 | 120 | /* 不推荐 */ 121 | a:focus, a:active { 122 | position: relative; top: 1px; 123 | } 124 | 125 | /* 推荐 */ 126 | h1, 127 | h2, 128 | h3 { 129 | font-weight: normal; 130 | line-height: 1.2; 131 | } 132 | 133 | 134 | CSS代码块分离 135 | ----------------- 136 | 137 | 使用新空行分离规则。 138 | 139 | 始终把一个空行(两个换行符)放在代码块规则之间。 140 | 141 | .. code-block:: css 142 | 143 | html { 144 | background: #fff; 145 | } 146 | 147 | 148 | body { 149 | margin: auto; 150 | width: 50%; 151 | } 152 | 153 | CSS引号 154 | ---------- 155 | 156 | 属性选择器和属性值中使用单引号。 157 | 158 | 在属性选择器及属性值中使用单引号('')而不是双引号("")。在 ``url()`` 中不要使用引号。 159 | 160 | 特例:如果你确实需要定义 ``@charset`` ,由于 `不允许使用单引号 `_ ,故请使用双引号。 161 | 162 | .. code-block:: css 163 | 164 | /* 不推荐 */ 165 | @import url("//www.google.com/css/maia.css"); 166 | 167 | html { 168 | font-family: "open sans", arial, sans-serif; 169 | } 170 | 171 | /* 推荐 */ 172 | @import url(//www.google.com/css/maia.css); 173 | 174 | html { 175 | font-family: 'open sans', arial, sans-serif; 176 | } 177 | -------------------------------------------------------------------------------- /google-shell-styleguide/naming_conventions.rst: -------------------------------------------------------------------------------- 1 | 命名约定 2 | ================================ 3 | 4 | 函数名 5 | -------------------- 6 | 7 | .. tip:: 8 | 使用小写字母,并用下划线分隔单词。使用双冒号 ``::`` 分隔库。函数名之后必须有圆括号。关键词 ``function`` 是可选的,但必须在一个项目中保持一致。 9 | 10 | 如果你正在写单个函数,请用小写字母来命名,并用下划线分隔单词。如果你正在写一个包,使用双冒号 ``::`` 来分隔包名。大括号必须和函数名位于同一行(就像在Google的其他语言一样),并且函数名和圆括号之间没有空格。 11 | 12 | .. code-block:: shell 13 | 14 | # Single function 15 | my_func() { 16 | ... 17 | } 18 | 19 | # Part of a package 20 | mypackage::my_func() { 21 | ... 22 | } 23 | 24 | 当函数名后存在 ``()`` 时,关键词 ``function`` 是多余的。但是其促进了函数的快速辨识。 25 | 26 | 变量名 27 | -------------------- 28 | 29 | .. tip:: 30 | 如函数名。 31 | 32 | 循环的变量名应该和循环的任何变量同样命名。 33 | 34 | .. code-block:: shell 35 | 36 | for zone in ${zones}; do 37 | something_with "${zone}" 38 | done 39 | 40 | 常量和环境变量名 41 | -------------------- 42 | 43 | .. tip:: 44 | 全部大写,用下划线分隔,声明在文件的顶部。 45 | 46 | 常量和任何导出到环境中的都应该大写。 47 | 48 | .. code-block:: shell 49 | 50 | # Constant 51 | readonly PATH_TO_FILES='/some/path' 52 | 53 | # Both constant and environment 54 | declare -xr ORACLE_SID='PROD' 55 | 56 | 第一次设置时有一些就变成了常量(例如,通过getopts)。因此,可以在getopts中或基于条件来设定常量,但之后应该立即设置其为只读。值得注意的是,在函数中 ``declare`` 不会对全局变量进行操作。所以推荐使用 ``readonly`` 和 ``export`` 来代替。 57 | 58 | .. code-block:: shell 59 | 60 | VERBOSE='false' 61 | while getopts 'v' flag; do 62 | case "${flag}" in 63 | v) VERBOSE='true' ;; 64 | esac 65 | done 66 | readonly VERBOSE 67 | 68 | 源文件名 69 | -------------------- 70 | 71 | .. tip:: 72 | 小写,如果需要的话使用下划线分隔单词。 73 | 74 | 这是为了和在Google中的其他代码风格保持一致: ``maketemplate`` 或者 ``make_template`` ,而不是 ``make-template`` 。 75 | 76 | 只读变量 77 | -------------------- 78 | 79 | .. tip:: 80 | 使用 ``readonly`` 或者 ``declare -r`` 来确保变量只读。 81 | 82 | 因为全局变量在shell中广泛使用,所以在使用它们的过程中捕获错误是很重要的。当你声明了一个变量,希望其只读,那么请明确指出。 83 | 84 | .. code-block:: shell 85 | 86 | zip_version="$(dpkg --status zip | grep Version: | cut -d ' ' -f 2)" 87 | if [[ -z "${zip_version}" ]]; then 88 | error_message 89 | else 90 | readonly zip_version 91 | fi 92 | 93 | 使用本地变量 94 | -------------------- 95 | 96 | .. tip:: 97 | 使用 ``local`` 声明特定功能的变量。声明和赋值应该在不同行。 98 | 99 | 使用 ``local`` 来声明局部变量以确保其只在函数内部和子函数中可见。这避免了污染全局命名空间和不经意间设置可能具有函数之外重要性的变量。 100 | 101 | 当赋值的值由命令替换提供时,声明和赋值必须分开。因为内建的 ``local`` 不会从命令替换中传递退出码。 102 | 103 | .. code-block:: shell 104 | 105 | my_func2() { 106 | local name="$1" 107 | 108 | # Separate lines for declaration and assignment: 109 | local my_var 110 | my_var="$(my_func)" || return 111 | 112 | # DO NOT do this: $? contains the exit code of 'local', not my_func 113 | local my_var="$(my_func)" 114 | [[ $? -eq 0 ]] || return 115 | 116 | ... 117 | } 118 | 119 | 函数位置 120 | -------------------- 121 | 122 | .. tip:: 123 | 将文件中所有的函数一起放在常量下面。不要在函数之间隐藏可执行代码。 124 | 125 | 如果你有函数,请将他们一起放在文件头部。只有includes, ``set`` 声明和常量设置可能在函数声明之前完成。不要在函数之间隐藏可执行代码。如果那样做,会使得代码在调试时难以跟踪并出现意想不到的讨厌结果。 126 | 127 | 主函数main 128 | -------------------- 129 | 130 | .. tip:: 131 | 对于包含至少一个其他函数的足够长的脚本,需要称为 ``main`` 的函数。 132 | 133 | 为了方便查找程序的开始,将主程序放入一个称为 ``main`` 的函数,作为最下面的函数。这使其和代码库的其余部分保持一致性,同时允许你定义更多变量为局部变量(如果主代码不是一个函数就不能这么做)。文件中最后的非注释行应该是对 ``main`` 函数的调用。 134 | 135 | .. code-block:: shell 136 | 137 | main "$@" 138 | 139 | 显然,对于仅仅是线性流的短脚本, ``main`` 是矫枉过正,因此是不需要的。 140 | 141 | -------------------------------------------------------------------------------- /google-html-css-styleguide/css_style_rules.rst: -------------------------------------------------------------------------------- 1 | css样式规则 2 | ================== 3 | 4 | CSS有效性 5 | ---------- 6 | 7 | 尽可能使用有效的CSS。 8 | 9 | 使用有效的CSS代码,除非在处理css验证器bug或者是专有的语法时。 10 | 11 | 使用诸如 `W3C CSS validator `_ 等工具验证测试。 12 | 13 | 使用有效的CSS代码是一个可衡量CSS代码质量的指标,可帮你找出不起作用可被删除的CSS代码,从而确保CSS的合理使用。 14 | 15 | id与class的命名 16 | ----------------- 17 | 18 | 使用有意义的或者通用的id和class名称 19 | 20 | 用能反映出元素目的或者通用的id、class名称,代替那些很表象的、难懂的名称。 21 | 22 | 如果名称需要是易懂的,或不容易被修改,应该首选特定的或者能反映出元素目的的名称。 23 | 24 | 通用的名称适用于非特殊元素或与兄弟元素无区别的元素。他们常被称为“辅助元素”。 25 | 26 | 使用功能性或者通用的名称,可减少不必要的文档或者模板变化。 27 | 28 | .. code-block:: css 29 | 30 | /* 不推荐:无意义 */ 31 | #yee-1901 {} 32 | 33 | 34 | /* 不推荐:表象 */ 35 | .button-green {} 36 | .clear {} 37 | 38 | /* 推荐:具体的 */ 39 | #gallery {} 40 | #login {} 41 | .video {} 42 | 43 | /* 推荐:通用的 */ 44 | .aux {} 45 | .alt {} 46 | 47 | id与class的命名规范 48 | ----------------------- 49 | 50 | ID和class命名要尽可能简短,但必要的话就别怕长。 51 | 52 | 尽可能简洁地传达id或者class名称的含义。 53 | 54 | 使用简洁的id或者class名称有助于提高可读性和代码效率。 55 | 56 | .. code-block:: css 57 | 58 | /* 不推荐 */ 59 | #navigation {} 60 | .atr {} 61 | 62 | /* 推荐 */ 63 | #nav {} 64 | .author {} 65 | 66 | 选择器的类型 67 | -------------- 68 | 69 | 应当避免在id和class前添加类型选择器。 70 | 71 | 除了必要情况下(例如辅助的类),不要将元素与id或class名称结合做为选择器。 72 | 73 | 避免不必要的祖先选择器也是出于 `性能原因 `_ 的考虑。 74 | 75 | .. code-block:: css 76 | 77 | /* 不推荐 */ 78 | ul#example {} 79 | div.error {} 80 | 81 | /* 推荐 */ 82 | #example {} 83 | .error {} 84 | 85 | 简写属性 86 | ------------ 87 | 88 | 尽可能使用简写的属性书写方式。 89 | 90 | CSS提供了多种属性 `简写 `_ 的方式(如 ``font`` ),即使只显式设置一个值,也应该尽可能地使用。 91 | 92 | 使用简写属性有助于提高代码效率及可读性。 93 | 94 | .. code-block:: css 95 | 96 | /* 不推荐 */ 97 | border-top-style: none; 98 | font-family: palatino, georgia, serif; 99 | font-size: 100%; 100 | line-height: 1.6; 101 | padding-bottom: 2em; 102 | padding-left: 1em; 103 | padding-right: 1em; 104 | padding-top: 0; 105 | 106 | /* 推荐 */ 107 | border-top: 0; 108 | font: 100%/1.6 palatino, georgia, serif; 109 | padding: 0 1em 2em; 110 | 111 | 112 | 0与单位 113 | ---------- 114 | 115 | 省略“0”后的单位。 116 | 117 | 除非必需,否则0后不要加单位。 118 | 119 | .. code-block:: css 120 | 121 | margin: 0; 122 | padding: 0; 123 | 124 | 前导0 125 | ----------- 126 | 127 | 省略前导“0”值。 128 | 129 | 在-1至1之间的值无需保留整数位的0。 130 | 131 | .. code-block:: css 132 | 133 | font-size: .8em; 134 | 135 | 136 | 十六进制表示法 137 | ---------------- 138 | 139 | 在可能的情况下使用3个字符的十六进制表示法。 140 | 141 | 对于可用3字符十六进制表示的颜色值,按此规则书写更短、更简洁。 142 | 143 | .. code-block:: css 144 | 145 | /* 不推荐 */ 146 | color: #eebbcc; 147 | 148 | /* 推荐 */ 149 | color: #ebc; 150 | 151 | 152 | 前缀选择器 153 | ------------ 154 | 155 | 加特定应用前缀(可选) 156 | 157 | 大型项目中以及嵌入在其它项目或外部网站上的代码需要给id和class添加前缀(命名空间)。使用短的、独特的标识符,并在其后跟一个破折号。 158 | 使用命名空间有助于防止命名冲突,可以让维护变得简单,例如在搜索和替换操作时。 159 | 160 | .. code-block:: css 161 | 162 | .adw-help {} /* AdWords */ 163 | #maia-note {} /* Maia */ 164 | 165 | 166 | id与class名称分隔符 167 | --------------------- 168 | 169 | 用连字符分隔ID和类名中的单词。 170 | 171 | 选择器中的词语和缩写中不要使用除了连字符以外的任何字符(包括空字符),以提高可理解性和可读性。 172 | 173 | .. code-block:: css 174 | 175 | /* 不推荐: 单词未分开 */ 176 | .demoimage {} 177 | 178 | /* 不推荐:使用下划线而不是连字符 */ 179 | .error_status {} 180 | 181 | /* 推荐 */ 182 | #video-id {} 183 | .ads-sample {} 184 | 185 | Hacks 186 | ------------ 187 | 188 | 请先尝试其他的方法,避免用户代理检测以及CSS的“hacks”。 189 | 190 | 进行用户代理检测或使用特殊的CSS选择器及hacks看起来是处理样式差异的捷径。但为了实现和保持高效性以及代码的可维护性,这两种方案应该放到最后考虑。换句话说,用户代理检测和使用hacks会增大项目推进的阻力,所以从项目的长远利益考虑应尽力避免。一旦允许并无顾忌地使用用户代理检测和hacks便很容易滥用,最终一发而不可收。 191 | -------------------------------------------------------------------------------- /google-html-css-styleguide/html_style_rules.rst: -------------------------------------------------------------------------------- 1 | HTML样式规则 2 | ============== 3 | 4 | 文档类型 5 | --------- 6 | 7 | 使用HTML5。 8 | 9 | HTML5(HTML语法)是所有HTML文档的首选: ```` 。 10 | 11 | (推荐使用HTML,即text/html。不要使用XHTML。XHTML,即 `application/xhtml+xml `_,缺乏浏览器和基础结构的支持,并且优化的空间比HTML小。) 12 | 虽然HTML闭合标签没有问题,但是不要自闭合空标签。即写 ``
`` 而不是 ``
`` 。 13 | 14 | 15 | HTML合法性 16 | ------------ 17 | 18 | 在可能的情况下使用合法的HTML。 19 | 20 | 使用合法的HTML代码,除非由于文件大小导致的不可达到的性能目标而不能使用。 21 | 22 | 利用已用工具对合法性进行测试,例如 `W3C HTML validator `_。 23 | 24 | 使用合法的HTML是一个可度量的基准质量属性,该属性有助于了解技术需求和约束,从而确保合理的HTML使用。 25 | 26 | .. code-block:: html 27 | 28 | 29 | Test 30 |
This is only a test. 31 | 32 | 33 | 34 | 35 | Test 36 |
This is only a test.
37 | 38 | 语义化 39 | -------- 40 | 41 | 根据HTML的目的使用它。 42 | 43 | 根据元素(有时被错误的叫做“标签”)被创造的用途使用他们。比如,对标题使用标题元素,对段落使用 ``p`` 元素,对锚点使用 ``a`` 元素等。 44 | 45 | 语义化的使用HTML对于可访问性、复用性和代码的高效性等因素非常重要。 46 | 47 | .. code-block:: html 48 | 49 | 50 |
All recommendations
51 | 52 | 53 | All recommendations 54 | 55 | 多媒体降级 56 | ------------ 57 | 58 | 为多媒体提供替代内容。 59 | 60 | 对于图片、视频、通过 ``canvas`` 实现的动画等多媒体来说,确保提供可访问的替代内容。对于图片,可提供有意义的替代文本( ``alt`` );对于视频和音频,如有条件可提供对白和字幕。 61 | 62 | 提供替代内容对辅助功能很重要:没有 ``alt`` ,一位盲人用户很难知道一张图片的内容,其他用户可能不能了解视频和音频的内容。 63 | (对于 ``alt`` 属性会引起冗余的图片和你不打算添加CSS的纯粹装饰性的图片,不用添加替代文本,写成 ``alt=""`` 即可。) 64 | 65 | .. code-block:: html 66 | 67 | 68 | 69 | 70 | 71 | Spreadsheet screenshot. 72 | 73 | 关注点分离 74 | ----------- 75 | 76 | 将结构、表现、行为分离。 77 | 78 | 严格保持结构(标识),表现(样式),行为(脚本)分离,尽量使三者之间的相互影响最小。 79 | 80 | 就是说,确保文档和模板只包含HTML,并且HTML只用来表现结构。把任何表现性的东西都移到样式表,任何行为性的东西都移到脚本中。 81 | 82 | 此外,尽可能少的从文档和模板中引用样式表和脚本来减少三者的相互影响。 83 | 84 | 结构、表现、行为分离对维护非常重要。更改HTML文档和模板总是比更新样式表和脚本成本更高。 85 | 86 | .. code-block:: html 87 | 88 | 89 | 90 | HTML sucks 91 | 92 | 93 | 94 |

HTML sucks

95 |

I’ve read about this on a few sites but now I’m sure: 96 | HTML is stupid!!1 97 |

I can’t believe there’s no way to control the styling of 98 | my website without doing everything all over again!
99 | 100 | 101 | 102 | My first CSS-only redesign 103 | 104 |

My first CSS-only redesign

105 |

I’ve read about this on a few sites but today I’m actually 106 | doing it: separating concerns and avoiding anything in the HTML of 107 | my website that is presentational. 108 |

It’s awesome! 109 | 110 | 实体引用 111 | ----------- 112 | 113 | 不要使用实体引用。 114 | 115 | 假设文件、编辑器和团队之间使用相同的编码(UTF-8),则没有必要使用例如 ``—`` 、 ``”`` 或 ``☺`` 这样的实体引用。 116 | 117 | 唯一的例外适用于HTML中具有特殊意义的字符(比如<和&),和控制或者隐藏的字符(比如不换行空格)。 118 | 119 | .. code-block:: html 120 | 121 | 122 | The currency symbol for the Euro is “&eur;”. 123 | 124 | 125 | The currency symbol for the Euro is "€". 126 | 127 | 可选的标签 128 | ------------ 129 | 130 | 省略可选的标签(可选)。 131 | 132 | 为了优化文件大小和可扫描,考虑省略可选标签。 `HTML5规范 `_ 定义了哪些标签可以被省略。 133 | 134 | (这种方法可能要求一段宽限期去建立一个更加广泛的准则,因为它和Web开发人员通常所了解的有着显著不同。考虑到一致性和简单性,最好省略所有可选标签。) 135 | 136 | .. code-block:: html 137 | 138 | 139 | 140 | 141 | 142 | Spending money, spending bytes 143 | 144 | 145 |

Sic.

146 | 147 | 148 | 149 | 150 | 151 | Saving money, saving bytes 152 |

Qed. 153 | 154 | type属性 155 | --------- 156 | 157 | 为样式表和脚本省略 ``type`` 属性。 158 | 159 | 引用样式表(除非不是使用CSS)和脚本(除非不是使用JavaScript)不要使用type属性。 160 | 161 | HTML5将 `text/css `_ 和 `text/javascript `_ 设置为默认值,在这种情况下指定type属性并不必要。甚至同样兼容老版本的浏览器。 162 | 163 | .. code-block:: html 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | 15 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 16 | 17 | help: 18 | @echo "Please use \`make ' where is one of" 19 | @echo " html to make standalone HTML files" 20 | @echo " dirhtml to make HTML files named index.html in directories" 21 | @echo " singlehtml to make a single large HTML file" 22 | @echo " pickle to make pickle files" 23 | @echo " json to make JSON files" 24 | @echo " htmlhelp to make HTML files and a HTML help project" 25 | @echo " qthelp to make HTML files and a qthelp project" 26 | @echo " devhelp to make HTML files and a Devhelp project" 27 | @echo " epub to make an epub" 28 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 29 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 30 | @echo " text to make text files" 31 | @echo " man to make manual pages" 32 | @echo " changes to make an overview of all changed/added/deprecated items" 33 | @echo " linkcheck to check all external links for integrity" 34 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 35 | 36 | clean: 37 | -rm -rf $(BUILDDIR)/* 38 | 39 | html: 40 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 41 | @echo 42 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 43 | 44 | dirhtml: 45 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 48 | 49 | singlehtml: 50 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 51 | @echo 52 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 53 | 54 | pickle: 55 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 56 | @echo 57 | @echo "Build finished; now you can process the pickle files." 58 | 59 | json: 60 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 61 | @echo 62 | @echo "Build finished; now you can process the JSON files." 63 | 64 | htmlhelp: 65 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 66 | @echo 67 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 68 | ".hhp project file in $(BUILDDIR)/htmlhelp." 69 | 70 | qthelp: 71 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 72 | @echo 73 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 74 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 75 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/google-styleguide.qhcp" 76 | @echo "To view the help file:" 77 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/google-styleguide.qhc" 78 | 79 | devhelp: 80 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 81 | @echo 82 | @echo "Build finished." 83 | @echo "To view the help file:" 84 | @echo "# mkdir -p $$HOME/.local/share/devhelp/google-styleguide" 85 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/google-styleguide" 86 | @echo "# devhelp" 87 | 88 | epub: 89 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 90 | @echo 91 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 92 | 93 | latex: 94 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 95 | @echo 96 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 97 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 98 | "(use \`make latexpdf' here to do that automatically)." 99 | 100 | latexpdf: 101 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 102 | @echo "Running LaTeX files through pdflatex..." 103 | make -C $(BUILDDIR)/latex all-pdf 104 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 105 | 106 | text: 107 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 108 | @echo 109 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 110 | 111 | man: 112 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 113 | @echo 114 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 115 | 116 | changes: 117 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 118 | @echo 119 | @echo "The overview file is in $(BUILDDIR)/changes." 120 | 121 | linkcheck: 122 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 123 | @echo 124 | @echo "Link check complete; look for any errors in the above output " \ 125 | "or in $(BUILDDIR)/linkcheck/output.txt." 126 | 127 | doctest: 128 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 129 | @echo "Testing of doctests in the sources finished, look at the " \ 130 | "results in $(BUILDDIR)/doctest/output.txt." 131 | -------------------------------------------------------------------------------- /google-objc-styleguide/index.rst: -------------------------------------------------------------------------------- 1 | 2 | Google Objective-C Style Guide 中文版 3 | ---------------------------------------- 4 | 5 | :版本: 2.36 6 | 7 | :原作者: 8 | .. line-block:: 9 | 10 | Mike Pinkerton 11 | Greg Miller 12 | Dave MacLachlan 13 | 14 | :翻译: 15 | .. line-block:: 16 | 17 | `ewangke `_ 18 | `Yang.Y `_ 19 | 20 | :项目主页: 21 | - `Google Style Guide `_ 22 | - `Google 开源项目风格指南 - 中文版 `_ 23 | 24 | 25 | 译者的话 26 | ======== 27 | 28 | ewanke 29 | ^^^^^^^^^^^^ 30 | 31 | 一直想翻译这个 `style guide `_ ,终于在周末花了7个小时的时间用vim敲出了HTML。很多术语的翻译很难,平时看的中文技术类书籍有限,对很多术语的中文译法不是很清楚,难免有不恰当之处,请读者指出并帮我改进:王轲 ”ewangke at gmail.com” 2011.03.27 32 | 33 | Yang.Y 34 | ^^^^^^^^^^^^ 35 | 36 | 对 Objective-C 的了解有限,凭着感觉和 C/C++ 方面的理解: 37 | 38 | * 把指南更新到 2.36 版本 39 | * 调整了一些术语和句子 40 | 41 | 42 | 背景介绍 43 | ======== 44 | 45 | Objective-C 是 C 语言的扩展,增加了动态类型和面对对象的特性。它被设计成具有易读易用的,支持复杂的面向对象设计的编程语言。它是 Mac OS X 以及 iPhone 的主要开发语言。 46 | 47 | Cocoa 是 Mac OS X 上主要的应用程序框架之一。它由一组 Objective-C 类组成,为快速开发出功能齐全的 Mac OS X 应用程序提供支持。 48 | 49 | 苹果公司已经有一份非常全面的 Objective-C 编码指南。Google 为 C++ 也写了一份类似的编码指南。而这份 Objective-C 指南则是苹果和 Google 常规建议的最佳结合。因此,在阅读本指南之前,请确定你已经阅读过: 50 | 51 | * `Apple’s Cocoa Coding Guidelines `_ 52 | 53 | * `Google’s Open Source C++ Style Guide `_ 54 | 55 | .. note:: 56 | 57 | 所有在 Google 的 C++ 风格指南中所禁止的事情,如未明确说明,也同样不能在Objective-C++ 中使用。 58 | 59 | 本文档的目的在于为所有的 Mac OS X 的代码提供编码指南及实践。许多准则是在实际的项目和小组中经过长期的演化、验证的。Google 开发的开源项目遵从本指南的要求。 60 | 61 | Google 已经发布了遵守本指南开源代码,它们属于 `Google Toolbox for Mac project `_ 项目(本文以缩写 GTM 指代)。GTM 代码库中的代码通常为了可以在不同项目中复用。 62 | 63 | 注意,本指南不是 Objective-C 教程。我们假定读者对 Objective-C 非常熟悉。如果你刚刚接触 Objective-C 或者需要温习,请阅读 `The Objective-C Programming Language `_ 。 64 | 65 | 例子 66 | ======== 67 | 68 | 都说一个例子顶上一千句话,我们就从一个例子开始,来感受一下编码的风格、留白以及命名等等。 69 | 70 | 一个头文件的例子,展示了在 ``@interface`` 声明中如何进行正确的注释以及留白。 71 | 72 | .. code-block:: objc 73 | 74 | // Foo.h 75 | // AwesomeProject 76 | // 77 | // Created by Greg Miller on 6/13/08. 78 | // Copyright 2008 Google, Inc. All rights reserved. 79 | // 80 | 81 | #import 82 | 83 | // A sample class demonstrating good Objective-C style. All interfaces, 84 | // categories, and protocols (read: all top-level declarations in a header) 85 | // MUST be commented. Comments must also be adjacent to the object they're 86 | // documenting. 87 | // 88 | // (no blank line between this comment and the interface) 89 | @interface Foo : NSObject { 90 | @private 91 | NSString *bar_; 92 | NSString *bam_; 93 | } 94 | 95 | // Returns an autoreleased instance of Foo. See -initWithBar: for details 96 | // about |bar|. 97 | + (id)fooWithBar:(NSString *)bar; 98 | 99 | // Designated initializer. |bar| is a thing that represents a thing that 100 | // does a thing. 101 | - (id)initWithBar:(NSString *)bar; 102 | 103 | // Gets and sets |bar_|. 104 | - (NSString *)bar; 105 | - (void)setBar:(NSString *)bar; 106 | 107 | // Does some work with |blah| and returns YES if the work was completed 108 | // successfully, and NO otherwise. 109 | - (BOOL)doWorkWithBlah:(NSString *)blah; 110 | 111 | @end 112 | 113 | 一个源文件的例子,展示了 ``@implementation`` 部分如何进行正确的注释、留白。同时也包括了基于引用实现的一些重要方法,如 ``getters`` 、 ``setters`` 、 ``init`` 以及 ``dealloc`` 。 114 | 115 | .. code-block:: objc 116 | 117 | // 118 | // Foo.m 119 | // AwesomeProject 120 | // 121 | // Created by Greg Miller on 6/13/08. 122 | // Copyright 2008 Google, Inc. All rights reserved. 123 | // 124 | 125 | #import "Foo.h" 126 | 127 | 128 | @implementation Foo 129 | 130 | + (id)fooWithBar:(NSString *)bar { 131 | return [[[self alloc] initWithBar:bar] autorelease]; 132 | } 133 | 134 | // Must always override super's designated initializer. 135 | - (id)init { 136 | return [self initWithBar:nil]; 137 | } 138 | 139 | - (id)initWithBar:(NSString *)bar { 140 | if ((self = [super init])) { 141 | bar_ = [bar copy]; 142 | bam_ = [[NSString alloc] initWithFormat:@"hi %d", 3]; 143 | } 144 | return self; 145 | } 146 | 147 | - (void)dealloc { 148 | [bar_ release]; 149 | [bam_ release]; 150 | [super dealloc]; 151 | } 152 | 153 | - (NSString *)bar { 154 | return bar_; 155 | } 156 | 157 | - (void)setBar:(NSString *)bar { 158 | [bar_ autorelease]; 159 | bar_ = [bar copy]; 160 | } 161 | 162 | - (BOOL)doWorkWithBlah:(NSString *)blah { 163 | // ... 164 | return NO; 165 | } 166 | 167 | @end 168 | 169 | 170 | 不要求在 ``@interface``、``@implementation`` 和 ``@end`` 前后空行。如果你在 ``@interface`` 声明了实例变量,则须在关括号 ``}`` 之后空一行。 171 | 172 | 除非接口和实现非常短,比如少量的私有方法或桥接类,空行方有助于可读性。 173 | -------------------------------------------------------------------------------- /google-shell-styleguide/features_and_bugs.rst: -------------------------------------------------------------------------------- 1 | 特性及错误 2 | ================================ 3 | 4 | 命令替换 5 | -------------------- 6 | 7 | .. tip:: 8 | 使用 ``$(command)`` 而不是反引号。 9 | 10 | 嵌套的反引号要求用反斜杠转义内部的反引号。而 ``$(command)`` 形式嵌套时不需要改变,而且更易于阅读。 11 | 12 | 例如: 13 | 14 | .. code-block:: shell 15 | 16 | # This is preferred: 17 | var="$(command "$(command1)")" 18 | 19 | # This is not: 20 | var="`command \`command1\``" 21 | 22 | test,[和[[ 23 | -------------------- 24 | 25 | .. tip:: 26 | 推荐使用 ``[[ ... ]]`` ,而不是 ``[`` , ``test`` , 和 ``/usr/bin/[`` 。 27 | 28 | 因为在 ``[[`` 和 ``]]`` 之间不会有路径名称扩展或单词分割发生,所以使用 ``[[ ... ]]`` 能够减少错误。而且 ``[[ ... ]]`` 允许正则表达式匹配,而 ``[ ... ]`` 不允许。 29 | 30 | .. code-block:: shell 31 | 32 | # This ensures the string on the left is made up of characters in the 33 | # alnum character class followed by the string name. 34 | # Note that the RHS should not be quoted here. 35 | # For the gory details, see 36 | # E14 at http://tiswww.case.edu/php/chet/bash/FAQ 37 | if [[ "filename" =~ ^[[:alnum:]]+name ]]; then 38 | echo "Match" 39 | fi 40 | 41 | # This matches the exact pattern "f*" (Does not match in this case) 42 | if [[ "filename" == "f*" ]]; then 43 | echo "Match" 44 | fi 45 | 46 | # This gives a "too many arguments" error as f* is expanded to the 47 | # contents of the current directory 48 | if [ "filename" == f* ]; then 49 | echo "Match" 50 | fi 51 | 52 | 测试字符串 53 | -------------------- 54 | 55 | .. tip:: 56 | 尽可能使用引用,而不是过滤字符串。 57 | 58 | Bash足以在测试中处理空字符串。所以,请使用空(非空)字符串测试,而不是填充字符,使得代码更易于阅读。 59 | 60 | .. code-block:: shell 61 | 62 | # Do this: 63 | if [[ "${my_var}" = "some_string" ]]; then 64 | do_something 65 | fi 66 | 67 | # -z (string length is zero) and -n (string length is not zero) are 68 | # preferred over testing for an empty string 69 | if [[ -z "${my_var}" ]]; then 70 | do_something 71 | fi 72 | 73 | # This is OK (ensure quotes on the empty side), but not preferred: 74 | if [[ "${my_var}" = "" ]]; then 75 | do_something 76 | fi 77 | 78 | # Not this: 79 | if [[ "${my_var}X" = "some_stringX" ]]; then 80 | do_something 81 | fi 82 | 83 | 为了避免对你测试的目的产生困惑,请明确使用`-z`或者`-n` 84 | 85 | .. code-block:: shell 86 | 87 | # Use this 88 | if [[ -n "${my_var}" ]]; then 89 | do_something 90 | fi 91 | 92 | # Instead of this as errors can occur if ${my_var} expands to a test 93 | # flag 94 | if [[ "${my_var}" ]]; then 95 | do_something 96 | fi 97 | 98 | 文件名的通配符扩展 99 | -------------------- 100 | 101 | .. tip:: 102 | 当进行文件名的通配符扩展时,请使用明确的路径。 103 | 104 | 因为文件名可能以 ``-`` 开头,所以使用扩展通配符 ``./*`` 比 ``*`` 来得安全得多。 105 | 106 | .. code-block:: shell 107 | 108 | # Here's the contents of the directory: 109 | # -f -r somedir somefile 110 | 111 | # This deletes almost everything in the directory by force 112 | psa@bilby$ rm -v * 113 | removed directory: `somedir' 114 | removed `somefile' 115 | 116 | # As opposed to: 117 | psa@bilby$ rm -v ./* 118 | removed `./-f' 119 | removed `./-r' 120 | rm: cannot remove `./somedir': Is a directory 121 | removed `./somefile' 122 | 123 | Eval 124 | -------------------- 125 | 126 | .. tip:: 127 | 应该避免使用eval。 128 | 129 | 当用于给变量赋值时,Eval解析输入,并且能够设置变量,但无法检查这些变量是什么。 130 | 131 | .. code-block:: shell 132 | 133 | # What does this set? 134 | # Did it succeed? In part or whole? 135 | eval $(set_my_variables) 136 | 137 | # What happens if one of the returned values has a space in it? 138 | variable="$(eval some_function)" 139 | 140 | 管道导向while循环 141 | -------------------- 142 | 143 | .. tip:: 144 | 请使用过程替换或者for循环,而不是管道导向while循环。在while循环中被修改的变量是不能传递给父shell的,因为循环命令是在一个子shell中运行的。 145 | 146 | 管道导向while循环中的隐式子shell使得追踪bug变得很困难。 147 | 148 | .. code-block:: shell 149 | 150 | last_line='NULL' 151 | your_command | while read line; do 152 | last_line="${line}" 153 | done 154 | 155 | # This will output 'NULL' 156 | echo "${last_line}" 157 | 158 | 如果你确定输入中不包含空格或者特殊符号(通常意味着不是用户输入的),那么可以使用一个for循环。 159 | 160 | .. code-block:: shell 161 | 162 | total=0 163 | # Only do this if there are no spaces in return values. 164 | for value in $(command); do 165 | total+="${value}" 166 | done 167 | 168 | 使用过程替换允许重定向输出,但是请将命令放入一个显式的子shell中,而不是bash为while循环创建的隐式子shell。 169 | 170 | .. code-block:: shell 171 | 172 | total=0 173 | last_file= 174 | while read count filename; do 175 | total+="${count}" 176 | last_file="${filename}" 177 | done < <(your_command | uniq -c) 178 | 179 | # This will output the second field of the last line of output from 180 | # the command. 181 | echo "Total = ${total}" 182 | echo "Last one = ${last_file}" 183 | 184 | 当不需要传递复杂的结果给父shell时可以使用while循环。这通常需要一些更复杂的“解析”。请注意简单的例子使用如awk这类工具可能更容易完成。当你特别不希望改变父shell的范围变量时这可能也是有用的。 185 | 186 | .. code-block:: shell 187 | 188 | # Trivial implementation of awk expression: 189 | # awk '$3 == "nfs" { print $2 " maps to " $1 }' /proc/mounts 190 | cat /proc/mounts | while read src dest type opts rest; do 191 | if [[ ${type} == "nfs" ]]; then 192 | echo "NFS ${dest} maps to ${src}" 193 | fi 194 | done 195 | -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | if NOT "%PAPER%" == "" ( 11 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 12 | ) 13 | 14 | if "%1" == "" goto help 15 | 16 | if "%1" == "help" ( 17 | :help 18 | echo.Please use `make ^` where ^ is one of 19 | echo. html to make standalone HTML files 20 | echo. dirhtml to make HTML files named index.html in directories 21 | echo. singlehtml to make a single large HTML file 22 | echo. pickle to make pickle files 23 | echo. json to make JSON files 24 | echo. htmlhelp to make HTML files and a HTML help project 25 | echo. qthelp to make HTML files and a qthelp project 26 | echo. devhelp to make HTML files and a Devhelp project 27 | echo. epub to make an epub 28 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 29 | echo. text to make text files 30 | echo. man to make manual pages 31 | echo. changes to make an overview over all changed/added/deprecated items 32 | echo. linkcheck to check all external links for integrity 33 | echo. doctest to run all doctests embedded in the documentation if enabled 34 | goto end 35 | ) 36 | 37 | if "%1" == "clean" ( 38 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 39 | del /q /s %BUILDDIR%\* 40 | goto end 41 | ) 42 | 43 | if "%1" == "html" ( 44 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 45 | if errorlevel 1 exit /b 1 46 | echo. 47 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 48 | goto end 49 | ) 50 | 51 | if "%1" == "dirhtml" ( 52 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 53 | if errorlevel 1 exit /b 1 54 | echo. 55 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 56 | goto end 57 | ) 58 | 59 | if "%1" == "singlehtml" ( 60 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 61 | if errorlevel 1 exit /b 1 62 | echo. 63 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 64 | goto end 65 | ) 66 | 67 | if "%1" == "pickle" ( 68 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 69 | if errorlevel 1 exit /b 1 70 | echo. 71 | echo.Build finished; now you can process the pickle files. 72 | goto end 73 | ) 74 | 75 | if "%1" == "json" ( 76 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished; now you can process the JSON files. 80 | goto end 81 | ) 82 | 83 | if "%1" == "htmlhelp" ( 84 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished; now you can run HTML Help Workshop with the ^ 88 | .hhp project file in %BUILDDIR%/htmlhelp. 89 | goto end 90 | ) 91 | 92 | if "%1" == "qthelp" ( 93 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 94 | if errorlevel 1 exit /b 1 95 | echo. 96 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 97 | .qhcp project file in %BUILDDIR%/qthelp, like this: 98 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\google-styleguide.qhcp 99 | echo.To view the help file: 100 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\google-styleguide.ghc 101 | goto end 102 | ) 103 | 104 | if "%1" == "devhelp" ( 105 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 106 | if errorlevel 1 exit /b 1 107 | echo. 108 | echo.Build finished. 109 | goto end 110 | ) 111 | 112 | if "%1" == "epub" ( 113 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 117 | goto end 118 | ) 119 | 120 | if "%1" == "latex" ( 121 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 122 | if errorlevel 1 exit /b 1 123 | echo. 124 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 125 | goto end 126 | ) 127 | 128 | if "%1" == "text" ( 129 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 130 | if errorlevel 1 exit /b 1 131 | echo. 132 | echo.Build finished. The text files are in %BUILDDIR%/text. 133 | goto end 134 | ) 135 | 136 | if "%1" == "man" ( 137 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 141 | goto end 142 | ) 143 | 144 | if "%1" == "changes" ( 145 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.The overview file is in %BUILDDIR%/changes. 149 | goto end 150 | ) 151 | 152 | if "%1" == "linkcheck" ( 153 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Link check complete; look for any errors in the above output ^ 157 | or in %BUILDDIR%/linkcheck/output.txt. 158 | goto end 159 | ) 160 | 161 | if "%1" == "doctest" ( 162 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 163 | if errorlevel 1 exit /b 1 164 | echo. 165 | echo.Testing of doctests in the sources finished, look at the ^ 166 | results in %BUILDDIR%/doctest/output.txt. 167 | goto end 168 | ) 169 | 170 | :end 171 | -------------------------------------------------------------------------------- /google-cpp-styleguide/functions.rst: -------------------------------------------------------------------------------- 1 | 4. 函数 2 | ------------ 3 | 4 | 4.1. 输入和输出 5 | ~~~~~~~~~~~~~~~~~~~~~~~~ 6 | 7 | **总述** 8 | 9 | 我们倾向于按值返回, 否则按引用返回。 避免返回指针, 除非它可以为空. 10 | 11 | **说明** 12 | 13 | C++ 函数由返回值提供天然的输出, 有时也通过输出参数(或输入/输出参数)提供. 我们倾向于使用返回值而不是输出参数: 它们提高了可读性, 并且通常提供相同或更好的性能. 14 | 15 | C/C++ 中的函数参数或者是函数的输入, 或者是函数的输出, 或兼而有之. 非可选输入参数通常是值参或 ``const`` 引用, 非可选输出参数或输入/输出参数通常应该是引用 (不能为空). 对于可选的参数, 通常使用 ``std::optional`` 来表示可选的按值输入, 使用 ``const`` 指针来表示可选的其他输入. 使用非常量指针来表示可选输出和可选输入/输出参数. 16 | 17 | 避免定义需要 ``const`` 引用参数去超出生命周期的函数, 因为 ``const`` 引用参数与临时变量绑定. 相反, 要找到某种方法来消除生命周期要求 (例如, 通过复制参数), 或者通过 ``const`` 指针传递它并记录生命周期和非空要求. 18 | 19 | 在排序函数参数时, 将所有输入参数放在所有输出参数之前. 特别要注意, 在加入新参数时不要因为它们是新参数就置于参数列表最后, 而是仍然要按照前述的规则, 即将新的输入参数也置于输出参数之前. 20 | 21 | 这并非一个硬性规定. 输入/输出参数 (通常是类或结构体) 让这个问题变得复杂. 并且, 有时候为了其他函数保持一致, 你可能不得不有所变通. 22 | 23 | 4.2. 编写简短函数 24 | ~~~~~~~~~~~~~~~~~~~~~~~~ 25 | 26 | **总述** 27 | 28 | 我们倾向于编写简短, 凝练的函数. 29 | 30 | **说明** 31 | 32 | 我们承认长函数有时是合理的, 因此并不硬性限制函数的长度. 如果函数超过 40 行, 可以思索一下能不能在不影响程序结构的前提下对其进行分割. 33 | 34 | 即使一个长函数现在工作的非常好, 一旦有人对其修改, 有可能出现新的问题, 甚至导致难以发现的 bug. 使函数尽量简短, 以便于他人阅读和修改代码. 35 | 36 | 在处理代码时, 你可能会发现复杂的长函数. 不要害怕修改现有代码: 如果证实这些代码使用 / 调试起来很困难, 或者你只需要使用其中的一小段代码, 考虑将其分割为更加简短并易于管理的若干函数. 37 | 38 | 4.3. 引用参数 39 | ~~~~~~~~~~~~~~~~~~~~~~ 40 | 41 | **总述** 42 | 43 | 所有按引用传递的参数必须加上 ``const``. 44 | 45 | **定义** 46 | 47 | 在 C 语言中, 如果函数需要修改变量的值, 参数必须为指针, 如 ``int foo(int *pval)``. 在 C++ 中, 函数还可以声明为引用参数: ``int foo(int &val)``. 48 | 49 | **优点** 50 | 51 | 定义引用参数可以防止出现 ``(*pval)++`` 这样丑陋的代码. 引用参数对于拷贝构造函数这样的应用也是必需的. 同时也更明确地不接受空指针. 52 | 53 | **缺点** 54 | 55 | 容易引起误解, 因为引用在语法上是值变量却拥有指针的语义. 56 | 57 | **结论** 58 | 59 | 函数参数列表中, 所有引用参数都必须是 ``const``: 60 | 61 | .. code-block:: c++ 62 | 63 | void Foo(const string &in, string *out); 64 | 65 | 事实上这在 Google Code 是一个硬性约定: 输入参数是值参或 ``const`` 引用, 输出参数为指针. 输入参数可以是 ``const`` 指针, 但决不能是非 ``const`` 的引用参数, 除非特殊要求, 比如 ``swap()``. 66 | 67 | 有时候, 在输入形参中用 ``const T*`` 指针比 ``const T&`` 更明智. 比如: 68 | 69 | * 可能会传递空指针. 70 | 71 | * 函数要把指针或对地址的引用赋值给输入形参. 72 | 73 | 总而言之, 大多时候输入形参往往是 ``const T&``. 若用 ``const T*`` 则说明输入另有处理. 所以若要使用 ``const T*``, 则应给出相应的理由, 否则会使得读者感到迷惑. 74 | 75 | .. _function-overloading: 76 | 77 | 4.4. 函数重载 78 | ~~~~~~~~~~~~~~~~~~~~~~ 79 | 80 | **总述** 81 | 82 | 若要使用函数重载, 则必须能让读者一看调用点就胸有成竹, 而不用花心思猜测调用的重载函数到底是哪一种. 这一规则也适用于构造函数. 83 | 84 | **定义** 85 | 86 | 你可以编写一个参数类型为 ``const string&`` 的函数, 然后用另一个参数类型为 ``const char*`` 的函数对其进行重载: 87 | 88 | .. code-block:: c++ 89 | 90 | class MyClass { 91 | public: 92 | void Analyze(const string &text); 93 | void Analyze(const char *text, size_t textlen); 94 | }; 95 | 96 | **优点** 97 | 98 | 通过重载参数不同的同名函数, 可以令代码更加直观. 模板化代码需要重载, 这同时也能为使用者带来便利. 99 | 100 | **缺点** 101 | 102 | 如果函数单靠不同的参数类型而重载 (acgtyrant 注:这意味着参数数量不变), 读者就得十分熟悉 C++ 五花八门的匹配规则, 以了解匹配过程具体到底如何. 另外, 如果派生类只重载了某个函数的部分变体, 继承语义就容易令人困惑. 103 | 104 | **结论** 105 | 106 | 如果打算重载一个函数, 可以试试改在函数名里加上参数信息. 例如, 用 ``AppendString()`` 和 ``AppendInt()`` 等, 而不是一口气重载多个 ``Append()``. 如果重载函数的目的是为了支持不同数量的同一类型参数, 则优先考虑使用 ``std::vector`` 以便使用者可以用 :ref:`列表初始化 ` 指定参数. 107 | 108 | 4.5. 缺省参数 109 | ~~~~~~~~~~~~~~~~~~~~~~ 110 | 111 | **总述** 112 | 113 | 只允许在非虚函数中使用缺省参数, 且必须保证缺省参数的值始终一致. 缺省参数与 :ref:`函数重载 ` 遵循同样的规则. 一般情况下建议使用函数重载, 尤其是在缺省函数带来的可读性提升不能弥补下文中所提到的缺点的情况下. 114 | 115 | **优点** 116 | 117 | 有些函数一般情况下使用默认参数, 但有时需要又使用非默认的参数. 缺省参数为这样的情形提供了便利, 使程序员不需要为了极少的例外情况编写大量的函数. 和函数重载相比, 缺省参数的语法更简洁明了, 减少了大量的样板代码, 也更好地区别了 "必要参数" 和 "可选参数". 118 | 119 | **缺点** 120 | 121 | 缺省参数实际上是函数重载语义的另一种实现方式, 因此所有 :ref:`不应当使用函数重载的理由 ` 也都适用于缺省参数. 122 | 123 | 虚函数调用的缺省参数取决于目标对象的静态类型, 此时无法保证给定函数的所有重载声明的都是同样的缺省参数. 124 | 125 | 缺省参数是在每个调用点都要进行重新求值的, 这会造成生成的代码迅速膨胀. 作为读者, 一般来说也更希望缺省的参数在声明时就已经被固定了, 而不是在每次调用时都可能会有不同的取值. 126 | 127 | 缺省参数会干扰函数指针, 导致函数签名与调用点的签名不一致. 而函数重载不会导致这样的问题. 128 | 129 | **结论** 130 | 131 | 对于虚函数, 不允许使用缺省参数, 因为在虚函数中缺省参数不一定能正常工作. 如果在每个调用点缺省参数的值都有可能不同, 在这种情况下缺省函数也不允许使用. (例如, 不要写像 ``void f(int n = counter++);`` 这样的代码.) 132 | 133 | 在其他情况下, 如果缺省参数对可读性的提升远远超过了以上提及的缺点的话, 可以使用缺省参数. 如果仍有疑惑, 就使用函数重载. 134 | 135 | 4.6. 函数返回类型后置语法 136 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 137 | 138 | **总述** 139 | 140 | 只有在常规写法 (返回类型前置) 不便于书写或不便于阅读时使用返回类型后置语法. 141 | 142 | **定义** 143 | 144 | C++ 现在允许两种不同的函数声明方式. 以往的写法是将返回类型置于函数名之前. 例如: 145 | 146 | .. code-block:: c++ 147 | 148 | int foo(int x); 149 | 150 | C++11 引入了这一新的形式. 现在可以在函数名前使用 ``auto`` 关键字, 在参数列表之后后置返回类型. 例如: 151 | 152 | .. code-block:: c++ 153 | 154 | auto foo(int x) -> int; 155 | 156 | 后置返回类型为函数作用域. 对于像 ``int`` 这样简单的类型, 两种写法没有区别. 但对于复杂的情况, 例如类域中的类型声明或者以函数参数的形式书写的类型, 写法的不同会造成区别. 157 | 158 | **优点** 159 | 160 | 后置返回类型是显式地指定 :ref:`Lambda 表达式 ` 的返回值的唯一方式. 某些情况下, 编译器可以自动推导出 Lambda 表达式的返回类型, 但并不是在所有的情况下都能实现. 即使编译器能够自动推导, 显式地指定返回类型也能让读者更明了. 161 | 162 | 有时在已经出现了的函数参数列表之后指定返回类型, 能够让书写更简单, 也更易读, 尤其是在返回类型依赖于模板参数时. 例如: 163 | 164 | .. code-block:: c++ 165 | 166 | template auto add(T t, U u) -> decltype(t + u); 167 | 168 | 对比下面的例子: 169 | 170 | .. code-block:: c++ 171 | 172 | template decltype(declval() + declval()) add(T t, U u); 173 | 174 | **缺点** 175 | 176 | 后置返回类型相对来说是非常新的语法, 而且在 C 和 Java 中都没有相似的写法, 因此可能对读者来说比较陌生. 177 | 178 | 在已有的代码中有大量的函数声明, 你不可能把它们都用新的语法重写一遍. 因此实际的做法只能是使用旧的语法或者新旧混用. 在这种情况下, 只使用一种版本是相对来说更规整的形式. 179 | 180 | **结论** 181 | 182 | 在大部分情况下, 应当继续使用以往的函数声明写法, 即将返回类型置于函数名前. 只有在必需的时候 (如 Lambda 表达式) 或者使用后置语法能够简化书写并且提高易读性的时候才使用新的返回类型后置语法. 但是后一种情况一般来说是很少见的, 大部分时候都出现在相当复杂的模板代码中, 而多数情况下不鼓励写这样 :ref:`复杂的模板代码 `. 183 | -------------------------------------------------------------------------------- /google-objc-styleguide/naming.rst: -------------------------------------------------------------------------------- 1 | 命名 2 | ========= 3 | 4 | 5 | 对于易维护的代码而言,命名规则非常重要。Objective-C 的方法名往往十分长,但代码块读起来就像散文一样,不需要太多的代码注释。 6 | 7 | 当编写纯粹的 Objective-C 代码时,我们基本遵守标准的 `Objective-C naming rules `_,这些命名规则可能与 C++ 风格指南中的大相径庭。例如,Google 的 C++ 风格指南中推荐使用下划线分隔的单词作为变量名,而(苹果的)风格指南则使用驼峰命名法,这在 Objective-C 社区中非常普遍。 8 | 9 | 任何的类、类别、方法以及变量的名字中都使用全大写的 `首字母缩写 `_。这遵守了苹果的标准命名方式,如 URL、TIFF 以及 EXIF。 10 | 11 | 当编写 Objective-C++ 代码时,事情就不这么简单了。许多项目需要实现跨平台的 C++ API,并混合一些 Objective-C、Cocoa 代码,或者直接以 C++ 为后端,前端用本地 Cocoa 代码。这就导致了两种命名方式直接不统一。 12 | 13 | 我们的解决方案是:编码风格取决于方法/函数以哪种语言实现。如果在一个 ``@implementation`` 语句中,就使用 Objective-C 的风格。如果实现一个 C++ 的类,就使用 C++ 的风格。这样避免了一个函数里面实例变量和局部变量命名规则混乱,严重影响可读性。 14 | 15 | 文件名 16 | ~~~~~~~~ 17 | 18 | .. tip:: 19 | 20 | 文件名须反映出其实现了什么类 -- 包括大小写。遵循你所参与项目的约定。 21 | 22 | 文件的扩展名应该如下: 23 | 24 | ======= ========================== 25 | ``.h`` C/C++/Objective-C 的头文件 26 | ``.m`` Objective-C 实现文件 27 | ``.mm`` Objective-C++ 的实现文件 28 | ``.cc`` 纯 C++ 的实现文件 29 | ``.c`` 纯 C 的实现文件 30 | ======= ========================== 31 | 32 | 类别的文件名应该包含被扩展的类名,如:``GTMNSString+Utils.h`` 或``GTMNSTextView+Autocomplete.h``。 33 | 34 | 35 | Objective-C++ 36 | ~~~~~~~~~~~~~~~~ 37 | 38 | .. tip:: 39 | 40 | 源代码文件内,Objective-C++ 代码遵循你正在实现的函数/方法的风格。 41 | 42 | 为了最小化 Cocoa/Objective-C 与 C++ 之间命名风格的冲突,根据待实现的函数/方法选择编码风格。实现 ``@implementation`` 语句块时,使用 Objective-C 的命名规则;如果实现一个 C++ 的类,就使用 C++ 命名规则。 43 | 44 | .. code-block:: objc 45 | 46 | // file: cross_platform_header.h 47 | 48 | class CrossPlatformAPI { 49 | public: 50 | ... 51 | int DoSomethingPlatformSpecific(); // impl on each platform 52 | private: 53 | int an_instance_var_; 54 | }; 55 | 56 | // file: mac_implementation.mm 57 | #include "cross_platform_header.h" 58 | 59 | // A typical Objective-C class, using Objective-C naming. 60 | @interface MyDelegate : NSObject { 61 | @private 62 | int instanceVar_; 63 | CrossPlatformAPI* backEndObject_; 64 | } 65 | - (void)respondToSomething:(id)something; 66 | @end 67 | @implementation MyDelegate 68 | - (void)respondToSomething:(id)something { 69 | // bridge from Cocoa through our C++ backend 70 | instanceVar_ = backEndObject->DoSomethingPlatformSpecific(); 71 | NSString* tempString = [NSString stringWithInt:instanceVar_]; 72 | NSLog(@"%@", tempString); 73 | } 74 | @end 75 | 76 | // The platform-specific implementation of the C++ class, using 77 | // C++ naming. 78 | int CrossPlatformAPI::DoSomethingPlatformSpecific() { 79 | NSString* temp_string = [NSString stringWithInt:an_instance_var_]; 80 | NSLog(@"%@", temp_string); 81 | return [temp_string intValue]; 82 | } 83 | 84 | 85 | 类名 86 | ~~~~~ 87 | 88 | .. tip:: 89 | 90 | 类名(以及类别、协议名)应首字母大写,并以驼峰格式分割单词。 91 | 92 | *应用层* 的代码,应该尽量避免不必要的前缀。为每个类都添加相同的前缀无助于可读性。当编写的代码期望在不同应用程序间复用时,应使用前缀(如:``GTMSendMessage``)。 93 | 94 | 95 | 类别名 96 | ~~~~~~ 97 | 98 | .. tip:: 99 | 100 | 类别名应该有两三个字母的前缀以表示类别是项目的一部分或者该类别是通用的。类别名应该包含它所扩展的类的名字。 101 | 102 | 比如我们要基于 ``NSString`` 创建一个用于解析的类别,我们将把类别放在一个名为 ``GTMNSString+Parsing.h`` 的文件中。类别本身命名为 ``GTMStringParsingAdditions`` (是的,我们知道类别名和文件名不一样,但是这个文件中可能存在多个不同的与解析有关类别)。类别中的方法应该以 ``gtm_myCategoryMethodOnAString:`` 为前缀以避免命名冲突,因为 Objective-C 只有一个名字空间。如果代码不会分享出去,也不会运行在不同的地址空间中,方法名字就不那么重要了。 103 | 104 | 类名与包含类别名的括号之间,应该以一个空格分隔。 105 | 106 | 107 | Objective-C 方法名 108 | ~~~~~~~~~~~~~~~~~~~~~~~~ 109 | 110 | .. tip:: 111 | 112 | 方法名应该以小写字母开头,并混合驼峰格式。每个具名参数也应该以小写字母开头。 113 | 114 | 方法名应尽量读起来就像句子,这表示你应该选择与方法名连在一起读起来通顺的参数名。(例如,``convertPoint:fromRect:`` 或 ``replaceCharactersInRange:withString:``)。详情参见 `Apple’s Guide to Naming Methods `_。 115 | 116 | 访问器方法应该与他们 ``要获取的`` 成员变量的名字一样,但不应该以get作为前缀。例如: 117 | 118 | .. code-block:: objc 119 | 120 | - (id)getDelegate; // AVOID 121 | - (id)delegate; // GOOD 122 | 123 | 这仅限于 Objective-C 的方法名。C++ 的方法与函数的命名规则应该遵从 C++ 风格指南中的规则。 124 | 125 | 126 | 变量名 127 | ~~~~~~~~~~ 128 | 129 | .. tip:: 130 | 131 | 变量名应该以小写字母开头,并使用驼峰格式。类的成员变量应该以下划线作为后缀。例如:``myLocalVariable``、``myInstanceVariable_``。如果不能使用 Objective-C 2.0 的 ``@property``,使用 KVO/KVC 绑定的成员变量可以以一个下划线作为前缀。 132 | 133 | 134 | 普通变量名 135 | ^^^^^^^^^^^^ 136 | 137 | 对于静态的属性(``int`` 或指针),不要使用匈牙利命名法。尽量为变量起一个描述性的名字。不要担心浪费列宽,因为让新的代码阅读者立即理解你的代码更重要。例如: 138 | 139 | * 错误的命名: 140 | 141 | .. code-block:: objc 142 | 143 | int w; 144 | int nerr; 145 | int nCompConns; 146 | tix = [[NSMutableArray alloc] init]; 147 | obj = [someObject object]; 148 | p = [network port]; 149 | 150 | * 正确的命名: 151 | 152 | .. code-block:: objc 153 | 154 | int numErrors; 155 | int numCompletedConnections; 156 | tickets = [[NSMutableArray alloc] init]; 157 | userInfo = [someObject object]; 158 | port = [network port]; 159 | 160 | 实例变量 161 | ^^^^^^^^^^^^ 162 | 163 | 实例变量应该混合大小写,并以下划线作为后缀,如 ``usernameTextField_``。然而,如果不能使用 Objective-C 2.0(操作系统版本的限制),并且使用了 KVO/KVC 绑定成员变量时,我们允许例外(译者注: ``KVO=Key Value Observing,KVC=Key Value Coding``)。这种情况下,可以以一个下划线作为成员变量名字的前缀,这是苹果所接受的键/值命名惯例。如果可以使用 Objective-C 2.0,``@property`` 以及 ``@synthesize`` 提供了遵从这一命名规则的解决方案。 164 | 165 | 166 | 常量 167 | ^^^^^^^^^^^^ 168 | 169 | 常量名(如宏定义、枚举、静态局部变量等)应该以小写字母 ``k`` 开头,使用驼峰格式分隔单词,如:``kInvalidHandle,kWritePerm``。 170 | 171 | 172 | -------------------------------------------------------------------------------- /google-objc-styleguide/spacing.rst: -------------------------------------------------------------------------------- 1 | 留白和格式 2 | ========== 3 | 4 | 空格 vs. 制表符 5 | ~~~~~~~~~~~~~~~~~ 6 | 7 | .. tip:: 8 | 9 | 只使用空格,且一次缩进两个空格。 10 | 11 | 我们使用空格缩进。不要在代码中使用制表符。你应该将编辑器设置成自动将制表符替换成空格。 12 | 13 | 行宽 14 | ~~~~~~~ 15 | 16 | .. tip:: 17 | 18 | 尽量让你的代码保持在 80 列之内。 19 | 20 | 我们深知 Objective-C 是一门繁冗的语言,在某些情况下略超 80 列可能有助于提高可读性,但这也只能是特例而已,不能成为开脱。 21 | 22 | 如果阅读代码的人认为把把某行行宽保持在 80 列仍然有不失可读性,你应该按他们说的去做。 23 | 24 | 我们意识到这条规则是有争议的,但很多已经存在的代码坚持了本规则,我们觉得保证一致性更重要。 25 | 26 | 通过设置 *Xcode > Preferences > Text Editing > Show page guide*,来使越界更容易被发现。 27 | 28 | 方法声明和定义 29 | ~~~~~~~~~~~~~~ 30 | 31 | .. tip:: 32 | 33 | - / + 和返回类型之间须使用一个空格,参数列表中只有参数之间可以有空格。 34 | 35 | 方法应该像这样: 36 | 37 | .. code-block:: objc 38 | 39 | - (void)doSomethingWithString:(NSString *)theString { 40 | ... 41 | } 42 | 43 | 星号前的空格是可选的。当写新的代码时,要与先前代码保持一致。 44 | 45 | 如果一行有非常多的参数,更好的方式是将每个参数单独拆成一行。如果使用多行,将每个参数前的冒号对齐。 46 | 47 | .. code-block:: objc 48 | 49 | - (void)doSomethingWith:(GTMFoo *)theFoo 50 | rect:(NSRect)theRect 51 | interval:(float)theInterval { 52 | ... 53 | } 54 | 55 | 当第一个关键字比其它的短时,保证下一行至少有 4 个空格的缩进。这样可以使关键字垂直对齐,而不是使用冒号对齐: 56 | 57 | .. code-block:: objc 58 | 59 | - (void)short:(GTMFoo *)theFoo 60 | longKeyword:(NSRect)theRect 61 | evenLongerKeyword:(float)theInterval { 62 | ... 63 | } 64 | 65 | 66 | 方法调用 67 | ~~~~~~~~~~~~~~ 68 | 69 | .. tip:: 70 | 71 | 方法调用应尽量保持与方法声明的格式一致。当格式的风格有多种选择时,新的代码要与已有代码保持一致。 72 | 73 | 调用时所有参数应该在同一行: 74 | 75 | .. code-block:: objc 76 | 77 | [myObject doFooWith:arg1 name:arg2 error:arg3]; 78 | 79 | 或者每行一个参数,以冒号对齐: 80 | 81 | .. code-block:: objc 82 | 83 | [myObject doFooWith:arg1 84 | name:arg2 85 | error:arg3]; 86 | 87 | 不要使用下面的缩进风格: 88 | 89 | .. code-block:: objc 90 | 91 | [myObject doFooWith:arg1 name:arg2 // some lines with >1 arg 92 | error:arg3]; 93 | 94 | [myObject doFooWith:arg1 95 | name:arg2 error:arg3]; 96 | 97 | [myObject doFooWith:arg1 98 | name:arg2 // aligning keywords instead of colons 99 | error:arg3]; 100 | 101 | 方法定义与方法声明一样,当关键字的长度不足以以冒号对齐时,下一行都要以四个空格进行缩进。 102 | 103 | .. code-block:: objc 104 | 105 | [myObj short:arg1 106 | longKeyword:arg2 107 | evenLongerKeyword:arg3]; 108 | 109 | 110 | ``@public`` 和 ``@private`` 111 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 112 | 113 | .. tip:: 114 | 115 | ``@public`` 和 ``@private`` 访问修饰符应该以一个空格缩进。 116 | 117 | 与 C++ 中的 ``public, private`` 以及 ``protected`` 非常相似。 118 | 119 | .. code-block:: objc 120 | 121 | @interface MyClass : NSObject { 122 | @public 123 | ... 124 | @private 125 | ... 126 | } 127 | @end 128 | 129 | 130 | 异常 131 | ~~~~~~~~~~ 132 | 133 | .. tip:: 134 | 135 | 每个 ``@`` 标签应该有独立的一行,在 ``@`` 与 ``{}`` 之间需要有一个空格, ``@catch`` 与被捕捉到的异常对象的声明之间也要有一个空格。 136 | 137 | 如果你决定使用 Objective-C 的异常,那么就按下面的格式。不过你最好先看看 :ref:`避免抛出异常 ` 了解下为什么不要使用异常。 138 | 139 | .. code-block:: objc 140 | 141 | @try { 142 | foo(); 143 | } 144 | @catch (NSException *ex) { 145 | bar(ex); 146 | } 147 | @finally { 148 | baz(); 149 | } 150 | 151 | 152 | 协议名 153 | ~~~~~~~~~~ 154 | 155 | .. tip:: 156 | 157 | 类型标识符和尖括号内的协议名之间,不能有任何空格。 158 | 159 | 160 | 这条规则适用于类声明、实例变量以及方法声明。例如: 161 | 162 | .. code-block:: objc 163 | 164 | @interface MyProtocoledClass : NSObject { 165 | @private 166 | id delegate_; 167 | } 168 | - (void)setDelegate:(id)aDelegate; 169 | @end 170 | 171 | 172 | 块(闭包) 173 | ~~~~~~~~~~ 174 | 175 | .. tip:: 176 | 177 | 块(block)适合用在 target/selector 模式下创建回调方法时,因为它使代码更易读。块中的代码应该缩进 4 个空格。 178 | 179 | 取决于块的长度,下列都是合理的风格准则: 180 | 181 | * 如果一行可以写完块,则没必要换行。 182 | * 如果不得不换行,关括号应与块声明的第一个字符对齐。 183 | * 块内的代码须按 4 空格缩进。 184 | * 如果块太长,比如超过 20 行,建议把它定义成一个局部变量,然后再使用该变量。 185 | * 如果块不带参数,``^{`` 之间无须空格。如果带有参数,``^(`` 之间无须空格,但 ``) {`` 之间须有一个空格。 186 | * 块内允许按两个空格缩进,但前提是和项目的其它代码保持一致的缩进风格。 187 | 188 | .. code-block:: objc 189 | 190 | // The entire block fits on one line. 191 | [operation setCompletionBlock:^{ [self onOperationDone]; }]; 192 | 193 | // The block can be put on a new line, indented four spaces, with the 194 | // closing brace aligned with the first character of the line on which 195 | // block was declared. 196 | [operation setCompletionBlock:^{ 197 | [self.delegate newDataAvailable]; 198 | }]; 199 | 200 | // Using a block with a C API follows the same alignment and spacing 201 | // rules as with Objective-C. 202 | dispatch_async(fileIOQueue_, ^{ 203 | NSString* path = [self sessionFilePath]; 204 | if (path) { 205 | // ... 206 | } 207 | }); 208 | 209 | // An example where the parameter wraps and the block declaration fits 210 | // on the same line. Note the spacing of |^(SessionWindow *window) {| 211 | // compared to |^{| above. 212 | [[SessionService sharedService] 213 | loadWindowWithCompletionBlock:^(SessionWindow *window) { 214 | if (window) { 215 | [self windowDidLoad:window]; 216 | } else { 217 | [self errorLoadingWindow]; 218 | } 219 | }]; 220 | 221 | // An example where the parameter wraps and the block declaration does 222 | // not fit on the same line as the name. 223 | [[SessionService sharedService] 224 | loadWindowWithCompletionBlock: 225 | ^(SessionWindow *window) { 226 | if (window) { 227 | [self windowDidLoad:window]; 228 | } else { 229 | [self errorLoadingWindow]; 230 | } 231 | }]; 232 | 233 | // Large blocks can be declared out-of-line. 234 | void (^largeBlock)(void) = ^{ 235 | // ... 236 | }; 237 | [operationQueue_ addOperationWithBlock:largeBlock]; 238 | 239 | -------------------------------------------------------------------------------- /google-cpp-styleguide/headers.rst: -------------------------------------------------------------------------------- 1 | 1. 头文件 2 | ---------------- 3 | 4 | 通常每一个 ``.cc`` 文件都有一个对应的 ``.h`` 文件. 也有一些常见例外, 如单元测试代码和只包含 ``main()`` 函数的 ``.cc`` 文件. 5 | 6 | 正确使用头文件可令代码在可读性、文件大小和性能上大为改观. 7 | 8 | 下面的规则将引导你规避使用头文件时的各种陷阱. 9 | 10 | .. _self-contained-headers: 11 | 12 | 1.1. Self-contained 头文件 13 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 14 | 15 | .. tip:: 16 | 17 | 头文件应该能够自给自足(self-contained,也就是可以作为第一个头文件被引入),以 ``.h`` 结尾。至于用来插入文本的文件,说到底它们并不是头文件,所以应以 ``.inc`` 结尾。不允许分离出 ``-inl.h`` 头文件的做法. 18 | 19 | 所有头文件要能够自给自足。换言之,用户和重构工具不需要为特别场合而包含额外的头文件。详言之,一个头文件要有 :ref:`define-guard`,统统包含它所需要的其它头文件,也不要求定义任何特别 symbols. 20 | 21 | 不过有一个例外,即一个文件并不是 self-contained 的,而是作为文本插入到代码某处。或者,文件内容实际上是其它头文件的特定平台(platform-specific)扩展部分。这些文件就要用 ``.inc`` 文件扩展名。 22 | 23 | 如果 ``.h`` 文件声明了一个模板或内联函数,同时也在该文件加以定义。凡是有用到这些的 ``.cc`` 文件,就得统统包含该头文件,否则程序可能会在构建中链接失败。不要把这些定义放到分离的 ``-inl.h`` 文件里(译者注:过去该规范曾提倡把定义放到 -inl.h 里过)。 24 | 25 | 有个例外:如果某函数模板为所有相关模板参数显式实例化,或本身就是某类的一个私有成员,那么它就只能定义在实例化该模板的 ``.cc`` 文件里。 26 | 27 | .. _define-guard: 28 | 29 | 1.2. #define 保护 30 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 31 | 32 | .. tip:: 33 | 34 | 所有头文件都应该有 ``#define`` 保护来防止头文件被多重包含, 命名格式当是: ``___H_`` . 35 | 36 | 为保证唯一性, 头文件的命名应该基于所在项目源代码树的全路径. 例如, 项目 ``foo`` 中的头文件 ``foo/src/bar/baz.h`` 可按如下方式保护: 37 | 38 | .. code-block:: c++ 39 | 40 | #ifndef FOO_BAR_BAZ_H_ 41 | #define FOO_BAR_BAZ_H_ 42 | ... 43 | #endif // FOO_BAR_BAZ_H_ 44 | 45 | .. _forward-declarations: 46 | 47 | 1.3. 前置声明 48 | ~~~~~~~~~~~~~~~~~~~~~~ 49 | 50 | .. tip:: 51 | 52 | 尽可能地避免使用前置声明。使用 ``#include`` 包含需要的头文件即可。 53 | 54 | **定义:** 55 | 56 | 所谓「前置声明」(forward declaration)是类、函数和模板的纯粹声明,没伴随着其定义. 57 | 58 | **优点:** 59 | 60 | * 前置声明能够节省编译时间,多余的 ``#include`` 会迫使编译器展开更多的文件,处理更多的输入。 61 | * 前置声明能够节省不必要的重新编译的时间。 ``#include`` 使代码因为头文件中无关的改动而被重新编译多次。 62 | 63 | **缺点:** 64 | 65 | * 前置声明隐藏了依赖关系,头文件改动时,用户的代码会跳过必要的重新编译过程。 66 | * 前置声明可能会被库的后续更改所破坏。前置声明函数或模板有时会妨碍头文件开发者变动其 API. 例如扩大形参类型,加个自带默认参数的模板形参等等。 67 | * 前置声明来自命名空间 ``std::`` 的 symbol 时,其行为未定义。 68 | * 很难判断什么时候该用前置声明,什么时候该用 ``#include`` 。极端情况下,用前置声明代替 ``#include`` 甚至都会暗暗地改变代码的含义: 69 | 70 | .. code-block:: c++ 71 | 72 | // b.h: 73 | struct B {}; 74 | struct D : B {}; 75 | 76 | // good_user.cc: 77 | #include "b.h" 78 | void f(B*); 79 | void f(void*); 80 | void test(D* x) { f(x); } // calls f(B*) 81 | 82 | 如果 ``#include`` 被 ``B`` 和 ``D`` 的前置声明替代, ``test()`` 就会调用 ``f(void*)`` . 83 | 84 | * 前置声明了不少来自头文件的 symbol 时,就会比单单一行的 ``include`` 冗长。 85 | * 仅仅为了能前置声明而重构代码(比如用指针成员代替对象成员)会使代码变得更慢更复杂. 86 | 87 | **结论:** 88 | 89 | * 尽量避免前置声明那些定义在其他项目中的实体. 90 | * 函数:总是使用 ``#include``. 91 | * 类模板:优先使用 ``#include``. 92 | 93 | 至于什么时候包含头文件,参见 :ref:`name-and-order-of-includes` 。 94 | 95 | .. _inline-functions: 96 | 97 | 1.4. 内联函数 98 | ~~~~~~~~~~~~~~~~~~~~~~ 99 | 100 | .. tip:: 101 | 102 | 只有当函数只有 10 行甚至更少时才将其定义为内联函数. 103 | 104 | **定义:** 105 | 106 | 当函数被声明为内联函数之后, 编译器会将其内联展开, 而不是按通常的函数调用机制进行调用. 107 | 108 | **优点:** 109 | 110 | 只要内联的函数体较小, 内联该函数可以令目标代码更加高效. 对于存取函数以及其它函数体比较短, 性能关键的函数, 鼓励使用内联. 111 | 112 | **缺点:** 113 | 114 | 滥用内联将导致程序变得更慢. 内联可能使目标代码量或增或减, 这取决于内联函数的大小. 内联非常短小的存取函数通常会减少代码大小, 但内联一个相当大的函数将戏剧性的增加代码大小. 现代处理器由于更好的利用了指令缓存, 小巧的代码往往执行更快。 115 | 116 | **结论:** 117 | 118 | 一个较为合理的经验准则是, 不要内联超过 10 行的函数. 谨慎对待析构函数, 析构函数往往比其表面看起来要更长, 因为有隐含的成员和基类析构函数被调用! 119 | 120 | 另一个实用的经验准则: 内联那些包含循环或 ``switch`` 语句的函数常常是得不偿失 (除非在大多数情况下, 这些循环或 ``switch`` 语句从不被执行). 121 | 122 | 有些函数即使声明为内联的也不一定会被编译器内联, 这点很重要; 比如虚函数和递归函数就不会被正常内联. 通常, 递归函数不应该声明成内联函数.(YuleFox 注: 递归调用堆栈的展开并不像循环那么简单, 比如递归层数在编译时可能是未知的, 大多数编译器都不支持内联递归函数). 虚函数内联的主要原因则是想把它的函数体放在类定义内, 为了图个方便, 抑或是当作文档描述其行为, 比如精短的存取函数. 123 | 124 | .. _name-and-order-of-includes: 125 | 126 | 1.5. ``#include`` 的路径及顺序 127 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 128 | 129 | .. tip:: 130 | 使用标准的头文件包含顺序可增强可读性, 避免隐藏依赖: 相关头文件, C 库, C++ 库, 其他库的 `.h`, 本项目内的 `.h`. 131 | 132 | 项目内头文件应按照项目源代码目录树结构排列, 避免使用 UNIX 特殊的快捷目录 ``.`` (当前目录) 或 ``..`` (上级目录). 例如, ``google-awesome-project/src/base/logging.h`` 应该按如下方式包含: 133 | 134 | .. code-block:: c++ 135 | 136 | #include "base/logging.h" 137 | 138 | 又如, ``dir/foo.cc`` 或 ``dir/foo_test.cc`` 的主要作用是实现或测试 ``dir2/foo2.h`` 的功能, ``foo.cc`` 中包含头文件的次序如下: 139 | 140 | #. ``dir2/foo2.h`` (优先位置, 详情如下) 141 | #. C 系统文件 142 | #. C++ 系统文件 143 | #. 其他库的 ``.h`` 文件 144 | #. 本项目内 ``.h`` 文件 145 | 146 | 这种优先的顺序排序保证当 ``dir2/foo2.h`` 遗漏某些必要的库时, ``dir/foo.cc`` 或 ``dir/foo_test.cc`` 的构建会立刻中止。因此这一条规则保证维护这些文件的人们首先看到构建中止的消息而不是维护其他包的人们。 147 | 148 | ``dir/foo.cc`` 和 ``dir2/foo2.h`` 通常位于同一目录下 (如 ``base/basictypes_unittest.cc`` 和 ``base/basictypes.h``), 但也可以放在不同目录下. 149 | 150 | 按字母顺序分别对每种类型的头文件进行二次排序是不错的主意。注意较老的代码可不符合这条规则,要在方便的时候改正它们。 151 | 152 | 您所依赖的符号 (symbols) 被哪些头文件所定义,您就应该包含(include)哪些头文件,`前置声明`__ (forward declarations) 情况除外。比如您要用到 ``bar.h`` 中的某个符号, 哪怕您所包含的 ``foo.h`` 已经包含了 ``bar.h``, 也照样得包含 ``bar.h``, 除非 ``foo.h`` 有明确说明它会自动向您提供 ``bar.h`` 中的 symbol. 不过,凡是 cc 文件所对应的「相关头文件」已经包含的,就不用再重复包含进其 cc 文件里面了,就像 ``foo.cc`` 只包含 ``foo.h`` 就够了,不用再管后者所包含的其它内容。 153 | 154 | __ forward-declarations_ 155 | 156 | 举例来说, ``google-awesome-project/src/foo/internal/fooserver.cc`` 的包含次序如下: 157 | 158 | .. code-block:: c++ 159 | 160 | #include "foo/public/fooserver.h" // 优先位置 161 | 162 | #include 163 | #include 164 | 165 | #include 166 | #include 167 | 168 | #include "base/basictypes.h" 169 | #include "base/commandlineflags.h" 170 | #include "foo/public/bar.h" 171 | 172 | **例外:** 173 | 174 | 有时,平台特定(system-specific)代码需要条件编译(conditional includes),这些代码可以放到其它 includes 之后。当然,您的平台特定代码也要够简练且独立,比如: 175 | 176 | .. code-block:: c++ 177 | 178 | #include "foo/public/fooserver.h" 179 | 180 | #include "base/port.h" // For LANG_CXX11. 181 | 182 | #ifdef LANG_CXX11 183 | #include 184 | #endif // LANG_CXX11 185 | 186 | 译者 (YuleFox) 笔记 187 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 188 | 189 | #. 避免多重包含是学编程时最基本的要求; 190 | #. 前置声明是为了降低编译依赖,防止修改一个头文件引发多米诺效应; 191 | #. 内联函数的合理使用可提高代码执行效率; 192 | #. ``-inl.h`` 可提高代码可读性 (一般用不到吧:D); 193 | #. 标准化函数参数顺序可以提高可读性和易维护性 (对函数参数的堆栈空间有轻微影响, 我以前大多是相同类型放在一起); 194 | #. 包含文件的名称使用 ``.`` 和 ``..`` 虽然方便却易混乱, 使用比较完整的项目路径看上去很清晰, 很条理, 包含文件的次序除了美观之外, 最重要的是可以减少隐藏依赖, 使每个头文件在 "最需要编译" (对应源文件处 :D) 的地方编译, 有人提出库文件放在最后, 这样出错先是项目内的文件, 头文件都放在对应源文件的最前面, 这一点足以保证内部错误的及时发现了. 195 | 196 | 译者(acgtyrant)笔记 197 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 198 | 199 | #. 原来还真有项目用 ``#includes`` 来插入文本,且其文件扩展名 ``.inc`` 看上去也很科学。 200 | #. Google 已经不再提倡 ``-inl.h`` 用法。 201 | #. 注意,前置声明的类是不完全类型(incomplete type),我们只能定义指向该类型的指针或引用,或者声明(但不能定义)以不完全类型作为参数或者返回类型的函数。毕竟编译器不知道不完全类型的定义,我们不能创建其类的任何对象,也不能声明成类内部的数据成员。 202 | #. 类内部的函数一般会自动内联。所以某函数一旦不需要内联,其定义就不要再放在头文件里,而是放到对应的 ``.cc`` 文件里。这样可以保持头文件的类相当精炼,也很好地贯彻了声明与定义分离的原则。 203 | #. 在 ``#include`` 中插入空行以分割相关头文件, C 库, C++ 库, 其他库的 ``.h`` 和本项目内的 ``.h`` 是个好习惯。 204 | -------------------------------------------------------------------------------- /google-shell-styleguide/formatting.rst: -------------------------------------------------------------------------------- 1 | 格式 2 | ================================ 3 | 4 | 缩进 5 | -------------------- 6 | 7 | .. tip:: 8 | 缩进两个空格,没有制表符。 9 | 10 | 在代码块之间请使用空行以提升可读性。缩进为两个空格。无论你做什么,请不要使用制表符。对于已有文件,保持已有的缩进格式。 11 | 12 | 行的长度和长字符串 13 | -------------------- 14 | 15 | .. tip:: 16 | 行的最大长度为80个字符。 17 | 18 | 如果你必须写长度超过80个字符的字符串,如果可能的话,尽量使用here document或者嵌入的换行符。长度超过80个字符的文字串且不能被合理地分割,这是正常的。但强烈建议找到一个方法使其变短。 19 | 20 | .. code-block:: shell 21 | 22 | # DO use 'here document's 23 | cat <` 的规则, 而非类型模板应当遵循 :ref:`变量命名 ` 的规则. 39 | 40 | 7.2. 文件命名 41 | ~~~~~~~~~~~~~~~~~~~~~~ 42 | 43 | **总述** 44 | 45 | 文件名要全部小写, 可以包含下划线 (``_``) 或连字符 (``-``), 依照项目的约定. 如果没有约定, 那么 "``_``" 更好. 46 | 47 | **说明** 48 | 49 | 可接受的文件命名示例: 50 | 51 | * ``my_useful_class.cc`` 52 | 53 | * ``my-useful-class.cc`` 54 | 55 | * ``myusefulclass.cc`` 56 | 57 | * ``myusefulclass_test.cc`` // ``_unittest`` 和 ``_regtest`` 已弃用. 58 | 59 | C++ 文件要以 ``.cc`` 结尾, 头文件以 ``.h`` 结尾. 专门插入文本的文件则以 ``.inc`` 结尾, 参见 :ref:`头文件自足 `. 60 | 61 | 不要使用已经存在于 ``/usr/include`` 下的文件名 (Yang.Y 注: 即编译器搜索系统头文件的路径), 如 ``db.h``. 62 | 63 | 通常应尽量让文件名更加明确. ``http_server_logs.h`` 就比 ``logs.h`` 要好. 定义类时文件名一般成对出现, 如 ``foo_bar.h`` 和 ``foo_bar.cc``, 对应于类 ``FooBar``. 64 | 65 | 内联函数定义必须放在 ``.h`` 文件中. 如果内联函数比较短, 就直接将实现也放在 ``.h`` 中. 66 | 67 | .. _type-names: 68 | 69 | 7.3. 类型命名 70 | ~~~~~~~~~~~~~~~~~~~~~~ 71 | 72 | **总述** 73 | 74 | 类型名称的每个单词首字母均大写, 不包含下划线: ``MyExcitingClass``, ``MyExcitingEnum``. 75 | 76 | **说明** 77 | 78 | 所有类型命名 —— 类, 结构体, 类型定义 (``typedef``), 枚举, 类型模板参数 —— 均使用相同约定, 即以大写字母开始, 每个单词首字母均大写, 不包含下划线. 例如: 79 | 80 | .. code-block:: c++ 81 | 82 | // 类和结构体 83 | class UrlTable { ... 84 | class UrlTableTester { ... 85 | struct UrlTableProperties { ... 86 | 87 | // 类型定义 88 | typedef hash_map PropertiesMap; 89 | 90 | // using 别名 91 | using PropertiesMap = hash_map; 92 | 93 | // 枚举 94 | enum UrlTableErrors { ... 95 | 96 | .. _variable-names: 97 | 98 | 7.4. 变量命名 99 | ~~~~~~~~~~~~~~~~~~~~~~ 100 | 101 | **总述** 102 | 103 | 变量 (包括函数参数) 和数据成员名一律小写, 单词之间用下划线连接. 类的成员变量以下划线结尾, 但结构体的就不用, 如: ``a_local_variable``, ``a_struct_data_member``, ``a_class_data_member_``. 104 | 105 | **说明** 106 | 107 | 普通变量命名 108 | ============================= 109 | 110 | 举例: 111 | 112 | .. code-block:: c++ 113 | 114 | string table_name; // 好 - 用下划线. 115 | string tablename; // 好 - 全小写. 116 | 117 | string tableName; // 差 - 混合大小写 118 | 119 | 类数据成员 120 | ============================= 121 | 122 | 不管是静态的还是非静态的, 类数据成员都可以和普通变量一样, 但要接下划线. 123 | 124 | .. code-block:: c++ 125 | 126 | class TableInfo { 127 | ... 128 | private: 129 | string table_name_; // 好 - 后加下划线. 130 | string tablename_; // 好. 131 | static Pool* pool_; // 好. 132 | }; 133 | 134 | 结构体变量 135 | ============================= 136 | 137 | 不管是静态的还是非静态的, 结构体数据成员都可以和普通变量一样, 不用像类那样接下划线: 138 | 139 | .. code-block:: c++ 140 | 141 | struct UrlTableProperties { 142 | string name; 143 | int num_entries; 144 | static Pool* pool; 145 | }; 146 | 147 | 结构体与类的使用讨论, 参考 :ref:`结构体 vs. 类 `. 148 | 149 | .. _constant-names: 150 | 151 | 7.5. 常量命名 152 | ~~~~~~~~~~~~~~~~~~~~~~ 153 | 154 | **总述** 155 | 156 | 声明为 ``constexpr`` 或 ``const`` 的变量, 或在程序运行期间其值始终保持不变的, 命名时以 "k" 开头, 大小写混合. 例如: 157 | 158 | .. code-block:: c++ 159 | 160 | const int kDaysInAWeek = 7; 161 | 162 | **说明** 163 | 164 | 所有具有静态存储类型的变量 (例如静态变量或全局变量, 参见 `存储类型 `_) 都应当以此方式命名. 对于其他存储类型的变量, 如自动变量等, 这条规则是可选的. 如果不采用这条规则, 就按照一般的变量命名规则. 165 | 166 | .. _function-names: 167 | 168 | 7.6. 函数命名 169 | ~~~~~~~~~~~~~~~~~~~~~~ 170 | 171 | **总述** 172 | 173 | 常规函数使用大小写混合, 取值和设值函数则要求与变量名匹配: ``MyExcitingFunction()``, ``MyExcitingMethod()``, ``my_exciting_member_variable()``, ``set_my_exciting_member_variable()``. 174 | 175 | **说明** 176 | 177 | 一般来说, 函数名的每个单词首字母大写 (即 "驼峰变量名" 或 "帕斯卡变量名"), 没有下划线. 对于首字母缩写的单词, 更倾向于将它们视作一个单词进行首字母大写 (例如, 写作 ``StartRpc()`` 而非 ``StartRPC()``). 178 | 179 | .. code-block:: c++ 180 | 181 | AddTableEntry() 182 | DeleteUrl() 183 | OpenFileOrDie() 184 | 185 | (同样的命名规则同时适用于类作用域与命名空间作用域的常量, 因为它们是作为 API 的一部分暴露对外的, 因此应当让它们看起来像是一个函数, 因为在这时, 它们实际上是一个对象而非函数的这一事实对外不过是一个无关紧要的实现细节.) 186 | 187 | 取值和设值函数的命名与变量一致. 一般来说它们的名称与实际的成员变量对应, 但并不强制要求. 例如 ``int count()`` 与 ``void set_count(int count)``. 188 | 189 | 7.7. 命名空间命名 190 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 191 | 192 | **总述** 193 | 194 | 命名空间以小写字母命名. 最高级命名空间的名字取决于项目名称. 要注意避免嵌套命名空间的名字之间和常见的顶级命名空间的名字之间发生冲突. 195 | 196 | 顶级命名空间的名称应当是项目名或者是该命名空间中的代码所属的团队的名字. 命名空间中的代码, 应当存放于和命名空间的名字匹配的文件夹或其子文件夹中. 197 | 198 | 注意 :ref:`不使用缩写作为名称 ` 的规则同样适用于命名空间. 命名空间中的代码极少需要涉及命名空间的名称, 因此没有必要在命名空间中使用缩写. 199 | 200 | 要避免嵌套的命名空间与常见的顶级命名空间发生名称冲突. 由于名称查找规则的存在, 命名空间之间的冲突完全有可能导致编译失败. 尤其是, 不要创建嵌套的 ``std`` 命名空间. 建议使用更独特的项目标识符 (``websearch::index``, ``websearch::index_util``) 而非常见的极易发生冲突的名称 (比如 ``websearch::util``). 201 | 202 | 对于 ``internal`` 命名空间, 要当心加入到同一 ``internal`` 命名空间的代码之间发生冲突 (由于内部维护人员通常来自同一团队, 因此常有可能导致冲突). 在这种情况下, 请使用文件名以使得内部名称独一无二 (例如对于 ``frobber.h``, 使用 ``websearch::index::frobber_internal``). 203 | 204 | 7.8. 枚举命名 205 | ~~~~~~~~~~~~~~~~~~~~~~ 206 | 207 | **总述** 208 | 209 | 枚举的命名应当和 :ref:`常量 ` 或 :ref:`宏 ` 一致: ``kEnumName`` 或是 ``ENUM_NAME``. 210 | 211 | **说明** 212 | 213 | 单独的枚举值应该优先采用 :ref:`常量 ` 的命名方式. 但 :ref:`宏 ` 方式的命名也可以接受. 枚举名 ``UrlTableErrors`` (以及 ``AlternateUrlTableErrors``) 是类型, 所以要用大小写混合的方式. 214 | 215 | .. code-block:: c++ 216 | 217 | enum UrlTableErrors { 218 | kOK = 0, 219 | kErrorOutOfMemory, 220 | kErrorMalformedInput, 221 | }; 222 | enum AlternateUrlTableErrors { 223 | OK = 0, 224 | OUT_OF_MEMORY = 1, 225 | MALFORMED_INPUT = 2, 226 | }; 227 | 228 | 2009 年 1 月之前, 我们一直建议采用 :ref:`宏 ` 的方式命名枚举值. 由于枚举值和宏之间的命名冲突, 直接导致了很多问题. 由此, 这里改为优先选择常量风格的命名方式. 新代码应该尽可能优先使用常量风格. 但是老代码没必要切换到常量风格, 除非宏风格确实会产生编译期问题. 229 | 230 | .. _macro-names: 231 | 232 | 7.9. 宏命名 233 | ~~~~~~~~~~~~~~~~~~ 234 | 235 | **总述** 236 | 237 | 你并不打算 :ref:`使用宏 `, 对吧? 如果你一定要用, 像这样命名: ``MY_MACRO_THAT_SCARES_SMALL_CHILDREN``. 238 | 239 | **说明** 240 | 241 | 参考 :ref:`预处理宏 `; 通常 *不应该* 使用宏. 如果不得不用, 其命名像枚举命名一样全部大写, 使用下划线: 242 | 243 | .. code-block:: c++ 244 | 245 | #define ROUND(x) ... 246 | #define PI_ROUNDED 3.0 247 | 248 | 7.10. 命名规则的特例 249 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 250 | 251 | **总述** 252 | 253 | 如果你命名的实体与已有 C/C++ 实体相似, 可参考现有命名策略. 254 | 255 | ``bigopen()``: 函数名, 参照 ``open()`` 的形式 256 | 257 | ``uint``: ``typedef`` 258 | 259 | ``bigpos``: ``struct`` 或 ``class``, 参照 ``pos`` 的形式 260 | 261 | ``sparse_hash_map``: STL 型实体; 参照 STL 命名约定 262 | 263 | ``LONGLONG_MAX``: 常量, 如同 ``INT_MAX`` 264 | 265 | 译者(acgtyrant)笔记 266 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 267 | 268 | #. 感觉 Google 的命名约定很高明, 比如写了简单的类 QueryResult, 接着又可以直接定义一个变量 query_result, 区分度很好; 再次, 类内变量以下划线结尾, 那么就可以直接传入同名的形参, 比如 ``TextQuery::TextQuery(std::string word) : word_(word) {}`` , 其中 ``word_`` 自然是类内私有成员. 269 | -------------------------------------------------------------------------------- /google-cpp-styleguide/scoping.rst: -------------------------------------------------------------------------------- 1 | 2. 作用域 2 | ---------------- 3 | 4 | .. _namespaces: 5 | 6 | 2.1. 命名空间 7 | ~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | .. tip:: 10 | 11 | 鼓励在 ``.cc`` 文件内使用匿名命名空间或 ``static`` 声明. 使用具名的命名空间时, 其名称可基于项目名或相对路径. 禁止使用 using 指示(using-directive)。禁止使用内联命名空间(inline namespace)。 12 | 13 | **定义:** 14 | 15 | 命名空间将全局作用域细分为独立的, 具名的作用域, 可有效防止全局作用域的命名冲突. 16 | 17 | **优点:** 18 | 19 | 虽然类已经提供了(可嵌套的)命名轴线 (YuleFox 注: 将命名分割在不同类的作用域内), 命名空间在这基础上又封装了一层. 20 | 21 | 举例来说, 两个不同项目的全局作用域都有一个类 ``Foo``, 这样在编译或运行时造成冲突. 如果每个项目将代码置于不同命名空间中, ``project1::Foo`` 和 ``project2::Foo`` 作为不同符号自然不会冲突. 22 | 23 | 内联命名空间会自动把内部的标识符放到外层作用域,比如: 24 | 25 | .. code-block:: c++ 26 | 27 | namespace X { 28 | inline namespace Y { 29 | void foo(); 30 | } // namespace Y 31 | } // namespace X 32 | 33 | ``X::Y::foo()`` 与 ``X::foo()`` 彼此可代替。内联命名空间主要用来保持跨版本的 ABI 兼容性。 34 | 35 | **缺点:** 36 | 37 | 命名空间具有迷惑性, 因为它们使得区分两个相同命名所指代的定义更加困难。 38 | 39 | 内联命名空间很容易令人迷惑,毕竟其内部的成员不再受其声明所在命名空间的限制。内联命名空间只在大型版本控制里有用。 40 | 41 | 有时候不得不多次引用某个定义在许多嵌套命名空间里的实体,使用完整的命名空间会导致代码的冗长。 42 | 43 | 在头文件中使用匿名空间导致违背 C++ 的唯一定义原则 (One Definition Rule (ODR)). 44 | 45 | **结论:** 46 | 47 | 根据下文将要提到的策略合理使用命名空间. 48 | 49 | - 遵守 `命名空间命名 `_ 中的规则。 50 | 51 | - 像之前的几个例子中一样,在命名空间的最后注释出命名空间的名字。 52 | 53 | - 用命名空间把文件包含, `gflags `_ 的声明/定义, 以及类的前置声明以外的整个源文件封装起来, 以区别于其它命名空间: 54 | 55 | .. code-block:: c++ 56 | 57 | // .h 文件 58 | namespace mynamespace { 59 | 60 | // 所有声明都置于命名空间中 61 | // 注意不要使用缩进 62 | class MyClass { 63 | public: 64 | ... 65 | void Foo(); 66 | }; 67 | 68 | } // namespace mynamespace 69 | 70 | .. code-block:: c++ 71 | 72 | // .cc 文件 73 | namespace mynamespace { 74 | 75 | // 函数定义都置于命名空间中 76 | void MyClass::Foo() { 77 | ... 78 | } 79 | 80 | } // namespace mynamespace 81 | 82 | 更复杂的 ``.cc`` 文件包含更多, 更复杂的细节, 比如 gflags 或 using 声明。 83 | 84 | .. code-block:: c++ 85 | 86 | #include "a.h" 87 | 88 | DEFINE_FLAG(bool, someflag, false, "dummy flag"); 89 | 90 | namespace a { 91 | 92 | ...code for a... // 左对齐 93 | 94 | } // namespace a 95 | 96 | - 不要在命名空间 ``std`` 内声明任何东西, 包括标准库的类前置声明. 在 ``std`` 命名空间声明实体是未定义的行为, 会导致如不可移植. 声明标准库下的实体, 需要包含对应的头文件. 97 | 98 | - 不应该使用 *using 指示* 引入整个命名空间的标识符号。 99 | 100 | .. code-block:: c++ 101 | 102 | // 禁止 —— 污染命名空间 103 | using namespace foo; 104 | 105 | - 不要在头文件中使用 *命名空间别名* 除非显式标记内部命名空间使用。因为任何在头文件中引入的命名空间都会成为公开API的一部分。 106 | 107 | .. code-block:: c++ 108 | 109 | // 在 .cc 中使用别名缩短常用的命名空间 110 | namespace baz = ::foo::bar::baz; 111 | 112 | .. code-block:: c++ 113 | 114 | // 在 .h 中使用别名缩短常用的命名空间 115 | namespace librarian { 116 | namespace impl { // 仅限内部使用 117 | namespace sidetable = ::pipeline_diagnostics::sidetable; 118 | } // namespace impl 119 | 120 | inline void my_inline_function() { 121 | // 限制在一个函数中的命名空间别名 122 | namespace baz = ::foo::bar::baz; 123 | ... 124 | } 125 | } // namespace librarian 126 | 127 | - 禁止用内联命名空间 128 | 129 | .. _unnamed-namespace-and-static-variables: 130 | 131 | 2.2. 匿名命名空间和静态变量 132 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 133 | 134 | .. tip:: 135 | 136 | 在 ``.cc`` 文件中定义一个不需要被外部引用的变量时,可以将它们放在匿名命名空间或声明为 ``static`` 。但是不要在 ``.h`` 文件中这么做。 137 | 138 | **定义:** 139 | 140 | 所有置于匿名命名空间的声明都具有内部链接性,函数和变量可以经由声明为 ``static`` 拥有内部链接性,这意味着你在这个文件中声明的这些标识符都不能在另一个文件中被访问。即使两个文件声明了完全一样名字的标识符,它们所指向的实体实际上是完全不同的。 141 | 142 | **结论:** 143 | 144 | 推荐、鼓励在 ``.cc`` 中对于不需要在其他地方引用的标识符使用内部链接性声明,但是不要在 ``.h`` 中使用。 145 | 146 | 匿名命名空间的声明和具名的格式相同,在最后注释上 ``namespace`` : 147 | 148 | .. code-block:: c++ 149 | 150 | namespace { 151 | ... 152 | } // namespace 153 | 154 | .. _nonmember-static-member-and-global-functions: 155 | 156 | 2.3. 非成员函数、静态成员函数和全局函数 157 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 158 | 159 | .. tip:: 160 | 161 | 使用静态成员函数或命名空间内的非成员函数, 尽量不要用裸的全局函数. 将一系列函数直接置于命名空间中,不要用类的静态方法模拟出命名空间的效果,类的静态方法应当和类的实例或静态数据紧密相关. 162 | 163 | **优点:** 164 | 165 | 某些情况下, 非成员函数和静态成员函数是非常有用的, 将非成员函数放在命名空间内可避免污染全局作用域. 166 | 167 | **缺点:** 168 | 169 | 将非成员函数和静态成员函数作为新类的成员或许更有意义, 当它们需要访问外部资源或具有重要的依赖关系时更是如此. 170 | 171 | **结论:** 172 | 173 | 有时, 把函数的定义同类的实例脱钩是有益的, 甚至是必要的. 这样的函数可以被定义成静态成员, 或是非成员函数. 非成员函数不应依赖于外部变量, 应尽量置于某个命名空间内. 相比单纯为了封装若干不共享任何静态数据的静态成员函数而创建类, 不如使用 :ref:`namespaces` 。举例而言,对于头文件 ``myproject/foo_bar.h`` , 应当使用 174 | 175 | .. code-block:: c++ 176 | 177 | namespace myproject { 178 | namespace foo_bar { 179 | void Function1(); 180 | void Function2(); 181 | } // namespace foo_bar 182 | } // namespace myproject 183 | 184 | 而非 185 | 186 | .. code-block:: c++ 187 | 188 | namespace myproject { 189 | class FooBar { 190 | public: 191 | static void Function1(); 192 | static void Function2(); 193 | }; 194 | } // namespace myproject 195 | 196 | 定义在同一编译单元的函数, 被其他编译单元直接调用可能会引入不必要的耦合和链接时依赖; 静态成员函数对此尤其敏感. 可以考虑提取到新类中, 或者将函数置于独立库的命名空间内. 197 | 198 | 如果你必须定义非成员函数, 又只是在 ``.cc`` 文件中使用它, 可使用匿名 :ref:`namespaces` 或 ``static`` 链接关键字 (如 ``static int Foo() {...}``) 限定其作用域. 199 | 200 | .. _local-variables: 201 | 202 | 2.4. 局部变量 203 | ~~~~~~~~~~~~~~~~~~~~~~ 204 | 205 | .. tip:: 206 | 207 | 将函数变量尽可能置于最小作用域内, 并在变量声明时进行初始化. 208 | 209 | C++ 允许在函数的任何位置声明变量. 我们提倡在尽可能小的作用域中声明变量, 离第一次使用越近越好. 这使得代码浏览者更容易定位变量声明的位置, 了解变量的类型和初始值. 特别是,应使用初始化的方式替代声明再赋值, 比如: 210 | 211 | .. code-block:: c++ 212 | 213 | int i; 214 | i = f(); // 坏——初始化和声明分离 215 | 216 | .. code-block:: c++ 217 | 218 | int j = g(); // 好——初始化时声明 219 | 220 | .. code-block:: c++ 221 | 222 | vector v; 223 | v.push_back(1); // 用花括号初始化更好 224 | v.push_back(2); 225 | 226 | .. code-block:: c++ 227 | 228 | vector v = {1, 2}; // 好——v 一开始就初始化 229 | 230 | 231 | 属于 ``if``, ``while`` 和 ``for`` 语句的变量应当在这些语句中正常地声明,这样子这些变量的作用域就被限制在这些语句中了,举例而言: 232 | 233 | .. code-block:: c++ 234 | 235 | while (const char* p = strchr(str, '/')) str = p + 1; 236 | 237 | 238 | .. warning:: 有一个例外, 如果变量是一个对象, 每次进入作用域都要调用其构造函数, 每次退出作用域都要调用其析构函数. 这会导致效率降低. 239 | 240 | .. code-block:: c++ 241 | 242 | // 低效的实现 243 | for (int i = 0; i < 1000000; ++i) { 244 | Foo f; // 构造函数和析构函数分别调用 1000000 次! 245 | f.DoSomething(i); 246 | } 247 | 248 | 在循环作用域外面声明这类变量要高效的多: 249 | 250 | .. code-block:: c++ 251 | 252 | Foo f; // 构造函数和析构函数只调用 1 次 253 | for (int i = 0; i < 1000000; ++i) { 254 | f.DoSomething(i); 255 | } 256 | 257 | .. _static-and-global-variables: 258 | 259 | 2.5. 静态和全局变量 260 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 261 | 262 | .. tip:: 263 | 264 | 禁止定义静态储存周期非POD变量,禁止使用含有副作用的函数初始化POD全局变量,因为多编译单元中的静态变量执行时的构造和析构顺序是未明确的,这将导致代码的不可移植。 265 | 266 | 禁止使用类的 `静态储存周期 `_ 变量:由于构造和析构函数调用顺序的不确定性,它们会导致难以发现的 bug 。不过 ``constexpr`` 变量除外,毕竟它们又不涉及动态初始化或析构。 267 | 268 | 静态生存周期的对象,即包括了全局变量,静态变量,静态类成员变量和函数静态变量,都必须是原生数据类型 (POD : Plain Old Data): 即 int, char 和 float, 以及 POD 类型的指针、数组和结构体。 269 | 270 | 静态变量的构造函数、析构函数和初始化的顺序在 C++ 中是只有部分明确的,甚至随着构建变化而变化,导致难以发现的 bug. 所以除了禁用类类型的全局变量,我们也不允许用函数返回值来初始化 POD 变量,除非该函数(比如 ``getenv()`` 或 ``getpid()`` )不涉及任何全局变量。函数作用域里的静态变量除外,毕竟它的初始化顺序是有明确定义的,而且只会在指令执行到它的声明那里才会发生。 271 | 272 | .. note:: Xris 译注: 273 | 274 | 同一个编译单元内是明确的,静态初始化优先于动态初始化,初始化顺序按照声明顺序进行,销毁则逆序。不同的编译单元之间初始化和销毁顺序属于未明确行为 (unspecified behaviour)。 275 | 276 | 277 | 同理,全局和静态变量在程序中断时会被析构,无论所谓中断是从 ``main()`` 返回还是对 ``exit()`` 的调用。析构顺序正好与构造函数调用的顺序相反。但既然构造顺序未定义,那么析构顺序当然也就不定了。比如,在程序结束时某静态变量已经被析构了,但代码还在跑——比如其它线程——并试图访问它且失败;再比如,一个静态 string 变量也许会在一个引用了前者的其它变量析构之前被析构掉。 278 | 279 | 改善以上析构问题的办法之一是用 ``quick_exit()`` 来代替 ``exit()`` 并中断程序。它们的不同之处是前者不会执行任何析构,也不会执行 ``atexit()`` 所绑定的任何 handlers. 如果您想在执行 ``quick_exit()`` 来中断时执行某 handler(比如刷新 log),您可以把它绑定到 ``_at_quick_exit()``. 如果您想在 ``exit()`` 和 ``quick_exit()`` 都用上该 handler, 都绑定上去。 280 | 281 | 综上所述,我们只允许 POD 类型的静态变量,即完全禁用 ``vector`` (使用 C 数组替代) 和 ``string`` (使用 ``const char []``)。 282 | 283 | 如果您确实需要一个 ``class`` 类型的静态或全局变量,可以考虑在 ``main()`` 函数或 ``pthread_once()`` 内初始化一个指针且永不回收。注意只能用 raw 指针,别用智能指针,毕竟后者的析构函数涉及到上文指出的不定顺序问题。 284 | 285 | .. note:: Yang.Y 译注: 286 | 287 | 上文提及的静态变量泛指静态生存周期的对象, 包括: 全局变量, 静态变量, 静态类成员变量, 以及函数静态变量. 288 | 289 | 译者 (YuleFox) 笔记 290 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 291 | 292 | #. ``cc`` 中的匿名命名空间可避免命名冲突, 限定作用域, 避免直接使用 ``using`` 关键字污染命名空间; 293 | #. 嵌套类符合局部使用原则, 只是不能在其他头文件中前置声明, 尽量不要 ``public``; 294 | #. 尽量不用全局函数和全局变量, 考虑作用域和命名空间限制, 尽量单独形成编译单元; 295 | #. 多线程中的全局变量 (含静态成员变量) 不要使用 ``class`` 类型 (含 STL 容器), 避免不明确行为导致的 bug. 296 | #. 作用域的使用, 除了考虑名称污染, 可读性之外, 主要是为降低耦合, 提高编译/执行效率. 297 | 298 | 译者(acgtyrant)笔记 299 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 300 | 301 | #. 注意「using 指示(using-directive)」和「using 声明(using-declaration)」的区别。 302 | #. 匿名命名空间说白了就是文件作用域,就像 C static 声明的作用域一样,后者已经被 C++ 标准提倡弃用。 303 | #. 局部变量在声明的同时进行显式值初始化,比起隐式初始化再赋值的两步过程要高效,同时也贯彻了计算机体系结构重要的概念「局部性(locality)」。 304 | #. 注意别在循环犯大量构造和析构的低级错误。 305 | -------------------------------------------------------------------------------- /google-typescript-styleguide/source_organization.rst: -------------------------------------------------------------------------------- 1 | 代码管理 2 | ################################################################################ 3 | 4 | .. _ts-modules: 5 | 6 | 模块 7 | ******************************************************************************** 8 | 9 | .. _import-paths: 10 | 11 | 导入路径 12 | ================================================================================ 13 | 14 | TypeScript 代码必须使用路径进行导入。这里的路径既可以是相对路径,以 ``.`` 或 ``..`` 开头,也可以是从项目根目录开始的绝对路径,如 ``root/path/to/file`` 。 15 | 16 | 在引用逻辑上属于同一项目的文件时,应使用相对路径 ``./foo`` ,不要使用绝对路径 ``path/to/foo`` 。 17 | 18 | 应尽可能地限制父层级的数量(避免出现诸如 ``../../../`` 的路径),过多的层级会导致模块和路径结构难以理解。 19 | 20 | .. code-block:: typescript 21 | 22 | import {Symbol1} from 'google3/path/from/root'; 23 | import {Symbol2} from '../parent/file'; 24 | import {Symbol3} from './sibling'; 25 | 26 | .. _namespaces-vs-modules: 27 | 28 | 用 命名空间 还是 模块? 29 | ================================================================================ 30 | 31 | 在 TypeScript 有两种组织代码的方式:命名空间(namespace)和模块(module)。 32 | 33 | 不允许使用命名空间,在 TypeScript 中必须使用模块(即 `ES6 模块 `_ )。也就是说,在引用其它文件中的代码时必须以 ``import {foo} from 'bar'`` 的形式进行导入和导出。 34 | 35 | 不允许使用 ``namespace Foo { ... }`` 的形式组织代码。命名空间只能在所用的外部第三方库有要求时才能使用。如果需要在语义上对代码划分命名空间,应当通过分成不同文件的方式实现。 36 | 37 | 不允许在导入时使用 ``require`` 关键字(形如 ``import x = require('...');`` )。应当使用 ES6 的模块语法。 38 | 39 | .. code-block:: typescript 40 | 41 | // 不要这样做!不要使用命名空间! 42 | namespace Rocket { 43 | function launch() { ... } 44 | } 45 | 46 | // 不要这样做!不要使用 ! 47 | /// 48 | 49 | // 不要这样做!不要使用 require() ! 50 | import x = require('mydep'); 51 | 52 | .. tip:: 53 | 54 | TypeScript 的命名空间早期也被称为内部模块并使用 ``module`` 关键字,形如 ``module Foo { ... }`` 。不要使用这种用法。任何时候都应当使用 ES6 的导入语法。 55 | 56 | .. _ts-exports: 57 | 58 | 导出 59 | ******************************************************************************** 60 | 61 | 代码中必须使用具名的导出声明。 62 | 63 | .. code-block:: typescript 64 | 65 | // Use named exports: 66 | export class Foo { ... } 67 | 68 | 不要使用默认导出,这样能保证所有的导入语句都遵循统一的范式: 69 | 70 | .. code-block:: typescript 71 | 72 | // 不要这样做!不要使用默认导出! 73 | export default class Foo { ... } 74 | 75 | 为什么?因为默认导出并不为被导出的符号提供一个标准的名称,这增加了维护的难度和降低可读性的风险,同时并未带来明显的益处。如下面的例子所示: 76 | 77 | .. code-block:: typescript 78 | 79 | // 默认导出会造成如下的弊端 80 | import Foo from './bar'; // 这个语句是合法的。 81 | import Bar from './bar'; // 这个语句也是合法的。 82 | 83 | 具名导出的一个优势是,当代码中试图导入一个并未被导出的符号时,这段代码会报错。例如,假设在 ``foo.ts`` 中有如下的导出声明: 84 | 85 | .. code-block:: typescript 86 | 87 | // 不要这样做! 88 | const foo = 'blah'; 89 | export default foo; 90 | 91 | 如果在 ``bar.ts`` 中有如下的导入语句: 92 | 93 | .. code-block:: typescript 94 | 95 | // 编译错误! 96 | import {fizz} from './foo'; 97 | 98 | 会导致编译错误: ``error TS2614: Module '"./foo"' has no exported member 'fizz'`` 。反之,如果在 ``bar.ts`` 中的导入语句为: 99 | 100 | .. code-block:: typescript 101 | 102 | // 不要这样做!这定义了一个多余的变量 fizz! 103 | import fizz from './foo'; 104 | 105 | 结果是 ``fizz === foo`` ,这往往不符合预期,且难以调试。 106 | 107 | 此外,默认导出会鼓励程序员将所有内容全部置于一个巨大的对象当中,这个对象实际上充当了命名空间的角色: 108 | 109 | .. code-block:: typescript 110 | 111 | // 不要这样做! 112 | export default class Foo { 113 | static SOME_CONSTANT = ... 114 | static someHelpfulFunction() { ... } 115 | ... 116 | } 117 | 118 | 显然,这个文件中具有文件作用域,它可以被用做命名空间。但是,这里创建了第二个作用域——类 ``Foo`` ,这个类在其它文件中具有歧义:它既可以被视为类型,又可以被视为值。 119 | 120 | 因此,应当使用文件作用域作为实质上的命名空间,同时使用具名的导出声明: 121 | 122 | .. code-block:: typescript 123 | 124 | // 应当这样做! 125 | export const SOME_CONSTANT = ... 126 | export function someHelpfulFunction() 127 | export class Foo { 128 | // 只有类 Foo 中的内容 129 | } 130 | 131 | .. _ts-export-visibility: 132 | 133 | 导出可见性 134 | ================================================================================ 135 | 136 | TypeScript 不支持限制导出符号的可见性。因此,不要导出不用于模块以外的符号。一般来说,应当尽量减小模块的外部 API 的规模。 137 | 138 | .. _ts-mutable-exports: 139 | 140 | 可变导出 141 | ================================================================================ 142 | 143 | 虽然技术上可以实现,但是可变导出会造成难以理解和调试的代码,尤其是对于在多个模块中经过了多次重新导出的符号。这条规则的一个例子是,不允许使用 ``export let`` 。 144 | 145 | .. code-block:: typescript 146 | 147 | // 不要这样做! 148 | export let foo = 3; 149 | // 在纯 ES6 环境中,变量 foo 是一个可变值,导入了 foo 的代码会观察到它的值在一秒钟之后发生了改变。 150 | // 在 TypeScript 中,如果 foo 被另一个文件重新导出了,导入该文件的代码则不会观察到变化。 151 | window.setTimeout(() => { 152 | foo = 4; 153 | }, 1000 /* ms */); 154 | 155 | 如果确实需要允许外部代码对可变值进行访问,应当提供一个显式的取值器。 156 | 157 | .. code-block:: typescript 158 | 159 | // 应当这样做! 160 | let foo = 3; 161 | window.setTimeout(() => { 162 | foo = 4; 163 | }, 1000 /* ms */); 164 | // 使用显式的取值器对可变导出进行访问。 165 | export function getFoo() { return foo; }; 166 | 167 | 有一种常见的编程情景是,要根据某种特定的条件从两个值中选取其中一个进行导出:先检查条件,然后导出。这种情况下,应当保证模块中的代码执行完毕后,导出的结果就是确定的。 168 | 169 | .. code-block:: typescript 170 | 171 | function pickApi() { 172 | if (useOtherApi()) return OtherApi; 173 | return RegularApi; 174 | } 175 | export const SomeApi = pickApi(); 176 | 177 | 178 | .. _ts-container-classes: 179 | 180 | 容器类 181 | ================================================================================ 182 | 183 | 不要为了实现命名空间创建含有静态方法或属性的容器类。 184 | 185 | .. code-block:: typescript 186 | 187 | // 不要这样做! 188 | export class Container { 189 | static FOO = 1; 190 | static bar() { return 1; } 191 | } 192 | 193 | 应当将这些方法和属性设为单独导出的常数和函数。 194 | 195 | .. code-block:: typescript 196 | 197 | // 应当这样做! 198 | export const FOO = 1; 199 | export function bar() { return 1; } 200 | 201 | .. _ts-imports-source-organization: 202 | 203 | 导入 204 | ******************************************************************************** 205 | 206 | 在 ES6 和 TypeScript 中,导入语句共有四种变体: 207 | 208 | ======================================== ======================================== ======================================== 209 | 导入类型 示例 用途 210 | ======================================== ======================================== ======================================== 211 | 模块 ``import * as foo from '...';`` TypeScript 导入方式 212 | 解构 ``import {SomeThing} from '...';`` TypeScript 导入方式 213 | 默认 ``import SomeThing from '...';`` 只用于外部代码的特殊需求 214 | 副作用 ``import '...';`` 只用于加载某些库的副作用(例如自定义元素) 215 | ======================================== ======================================== ======================================== 216 | 217 | .. code-block:: typescript 218 | 219 | // 应当这样做!从这两种变体中选择较合适的一种(见下文)。 220 | import * as ng from '@angular/core'; 221 | import {Foo} from './foo'; 222 | 223 | // 只在有需要时使用默认导入。 224 | import Button from 'Button'; 225 | 226 | // 有时导入某些库是为了其代码执行时的副作用。 227 | import 'jasmine'; 228 | import '@polymer/paper-button'; 229 | 230 | .. _ts-module-versus-destructuring-imports: 231 | 232 | 选择模块导入还是解构导入? 233 | ================================================================================ 234 | 235 | 根据使用场景的不同,模块导入和解构导入分别有其各自的优势。 236 | 237 | 虽然模块导入语句中出现了通配符 ``*`` ,但模块导入并不能因此被视为其它语言中的通配符导入。相反地,模块导入语句为整个模块提供了一个名称,模块中的所有符号都通过这个名称进行访问,这为代码提供了更好的可读性,同时令模块中的所有符号可以进行自动补全。模块导入减少了导入语句的数量(模块中的所有符号都可以使用),降低了命名冲突的出现几率,同时还允许为被导入的模块提供一个简洁的名称。在从一个大型 API 中导入多个不同的符号时,模块导入语句尤其有用。 238 | 239 | 解构导入语句则为每一个被导入的符号提供一个局部的名称,这样在使用被导入的符号时,代码可以更简洁。对那些十分常用的符号,例如 Jasmine 的 ``describe`` 和 ``it`` 来说,这一点尤其有用。 240 | 241 | .. code-block:: typescript 242 | 243 | // 不要这样做!无意义地使用命名空间中的名称使得导入语句过于冗长。 244 | import {TableViewItem, TableViewHeader, TableViewRow, TableViewModel, 245 | TableViewRenderer} from './tableview'; 246 | let item: TableViewItem = ...; 247 | 248 | .. code-block:: typescript 249 | 250 | // 应当这样做!使用模块作为命名空间。 251 | import * as tableview from './tableview'; 252 | let item: tableview.Item = ...; 253 | 254 | .. code-block:: typescript 255 | 256 | import * as testing from './testing'; 257 | 258 | // 所有的测试都只会重复地使用相同的三个函数。 259 | // 如果只需要导入少数几个符号,而这些符号的使用频率又非常高的话, 260 | // 也可以考虑使用解构导入语句直接导入这几个符号(见下文)。 261 | testing.describe('foo', () => { 262 | testing.it('bar', () => { 263 | testing.expect(...); 264 | testing.expect(...); 265 | }); 266 | }); 267 | 268 | .. code-block:: typescript 269 | 270 | // 这样做更好!为这几个常用的函数提供局部变量名。 271 | import {describe, it, expect} from './testing'; 272 | 273 | describe('foo', () => { 274 | it('bar', () => { 275 | expect(...); 276 | expect(...); 277 | }); 278 | }); 279 | ... 280 | 281 | .. _ts-renaming-imports: 282 | 283 | 重命名导入 284 | ================================================================================ 285 | 286 | 在代码中,应当通过使用模块导入或重命名导出解决命名冲突。此外,在需要时,也可以使用重命名导入(例如 ``import {SomeThing as SomeOtherThing}`` )。 287 | 288 | 在以下几种情况下,重命名导入可能较为有用: 289 | 290 | 1. 避免与其它导入的符号产生命名冲突。 291 | 2. 被导入符号的名称是自动生成的。 292 | 3. 被导入符号的名称不能清晰地描述其自身,需要通过重命名提高代码的可读性,如将 RxJS 的 ``from`` 函数重命名为 ``observableFrom`` 。 293 | 294 | .. _ts-import-export-type: 295 | 296 | ``import type`` 和 ``export type`` 297 | ================================================================================ 298 | 299 | 不要使用 ``import type ... from`` 或者 ``export type ... from`` 。 300 | 301 | .. tip:: 302 | 303 | 这一规则不适用于导出类型定义,如 ``export type Foo = ...;`` 。 304 | 305 | .. code-block:: typescript 306 | 307 | // 不要这样做! 308 | import type {Foo} from './foo'; 309 | export type {Bar} from './bar'; 310 | 311 | 应当使用常规的导入语句。 312 | 313 | .. code-block:: typescript 314 | 315 | // 应当这样做! 316 | import {Foo} from './foo'; 317 | export {Bar} from './bar'; 318 | 319 | TypeScript 的工具链会自动区分用作类型的符号和用作值的符号。对于类型引用,工具链不会生成运行时加载的代码。这样做的原因是为了提供更好的开发体验,否则在 ``import type`` 和 ``import`` 之间反复切换会非常繁琐。同时, ``import type`` 并不提供任何保证,因为代码仍然可以通过其它的途径导入同一个依赖。 320 | 321 | 如果需要在运行时加载代码以执行其副作用,应使用 ``import '...'`` ,参见 :ref:`ts-imports-source-organization` 一节。 322 | 323 | 使用 ``export type`` 似乎可以避免将某个用作值的符号导出为 API。然而,和 ``import type`` 类似, ``export type`` 也不提供任何保证,因为外部代码仍然可以通过其它途径导入。如果需要拆分对 API 作为值的使用和作为类型的使用,并保证二者不被混用的话,应当显式地将其拆分成不同的符号,例如 ``UserService`` 和 ``AjaxUserService`` ,这样不容易造成错误,同时能更好地表达设计思路。 324 | 325 | .. _ts-organize-by-feature: 326 | 327 | 根据特征组织代码 328 | ******************************************************************************** 329 | 330 | 应当根据特征而非类型组织代码。例如,一个在线商城的代码应当按照 ``products`` , ``checkout`` , ``backend`` 等分类,而不是 ``views`` , ``models`` , ``controllers`` 。 331 | 332 | -------------------------------------------------------------------------------- /google-cpp-styleguide/comments.rst: -------------------------------------------------------------------------------- 1 | 8. 注释 2 | ------------ 3 | 4 | 注释虽然写起来很痛苦, 但对保证代码可读性至关重要. 下面的规则描述了如何注释以及在哪儿注释. 当然也要记住: 注释固然很重要, 但最好的代码应当本身就是文档. 有意义的类型名和变量名, 要远胜过要用注释解释的含糊不清的名字. 5 | 6 | 你写的注释是给代码读者看的, 也就是下一个需要理解你的代码的人. 所以慷慨些吧, 下一个读者可能就是你! 7 | 8 | 8.1. 注释风格 9 | ~~~~~~~~~~~~~~~~~~~~~~ 10 | 11 | **总述** 12 | 13 | 使用 ``//`` 或 ``/* */``, 统一就好. 14 | 15 | **说明** 16 | 17 | ``//`` 或 ``/* */`` 都可以; 但 ``//`` *更* 常用. 要在如何注释及注释风格上确保统一. 18 | 19 | 8.2. 文件注释 20 | ~~~~~~~~~~~~~~~~~~~~~~ 21 | 22 | **总述** 23 | 24 | 在每一个文件开头加入版权公告. 25 | 26 | 文件注释描述了该文件的内容. 如果一个文件只声明, 或实现, 或测试了一个对象, 并且这个对象已经在它的声明处进行了详细的注释, 那么就没必要再加上文件注释. 除此之外的其他文件都需要文件注释. 27 | 28 | **说明** 29 | 30 | 法律公告和作者信息 31 | ============================= 32 | 33 | 每个文件都应该包含许可证引用. 为项目选择合适的许可证版本.(比如, Apache 2.0, BSD, LGPL, GPL) 34 | 35 | 如果你对原始作者的文件做了重大修改, 请考虑删除原作者信息. 36 | 37 | 文件内容 38 | ============================= 39 | 40 | 如果一个 ``.h`` 文件声明了多个概念, 则文件注释应当对文件的内容做一个大致的说明, 同时说明各概念之间的联系. 一个一到两行的文件注释就足够了, 对于每个概念的详细文档应当放在各个概念中, 而不是文件注释中. 41 | 42 | 不要在 ``.h`` 和 ``.cc`` 之间复制注释, 这样的注释偏离了注释的实际意义. 43 | 44 | .. _class-comments: 45 | 46 | 8.3. 类注释 47 | ~~~~~~~~~~~~~~~~~~ 48 | 49 | **总述** 50 | 51 | 每个类的定义都要附带一份注释, 描述类的功能和用法, 除非它的功能相当明显. 52 | 53 | .. code-block:: c++ 54 | 55 | // Iterates over the contents of a GargantuanTable. 56 | // Example: 57 | // GargantuanTableIterator* iter = table->NewIterator(); 58 | // for (iter->Seek("foo"); !iter->done(); iter->Next()) { 59 | // process(iter->key(), iter->value()); 60 | // } 61 | // delete iter; 62 | class GargantuanTableIterator { 63 | ... 64 | }; 65 | 66 | **说明** 67 | 68 | 类注释应当为读者理解如何使用与何时使用类提供足够的信息, 同时应当提醒读者在正确使用此类时应当考虑的因素. 如果类有任何同步前提, 请用文档说明. 如果该类的实例可被多线程访问, 要特别注意文档说明多线程环境下相关的规则和常量使用. 69 | 70 | 如果你想用一小段代码演示这个类的基本用法或通常用法, 放在类注释里也非常合适. 71 | 72 | 如果类的声明和定义分开了(例如分别放在了 ``.h`` 和 ``.cc`` 文件中), 此时, 描述类用法的注释应当和接口定义放在一起, 描述类的操作和实现的注释应当和实现放在一起. 73 | 74 | 8.4. 函数注释 75 | ~~~~~~~~~~~~~~~~~~~~~~ 76 | 77 | **总述** 78 | 79 | 函数声明处的注释描述函数功能; 定义处的注释描述函数实现. 80 | 81 | **说明** 82 | 83 | 函数声明 84 | ============================= 85 | 86 | 基本上每个函数声明处前都应当加上注释, 描述函数的功能和用途. 只有在函数的功能简单而明显时才能省略这些注释(例如, 简单的取值和设值函数). 注释使用叙述式 ("Opens the file") 而非指令式 ("Open the file"); 注释只是为了描述函数, 而不是命令函数做什么. 通常, 注释不会描述函数如何工作. 那是函数定义部分的事情. 87 | 88 | 函数声明处注释的内容: 89 | 90 | - 函数的输入输出. 91 | 92 | - 对类成员函数而言: 函数调用期间对象是否需要保持引用参数, 是否会释放这些参数. 93 | 94 | - 函数是否分配了必须由调用者释放的空间. 95 | 96 | - 参数是否可以为空指针. 97 | 98 | - 是否存在函数使用上的性能隐患. 99 | 100 | - 如果函数是可重入的, 其同步前提是什么? 101 | 102 | 举例如下: 103 | 104 | .. code-block:: c++ 105 | 106 | // Returns an iterator for this table. It is the client's 107 | // responsibility to delete the iterator when it is done with it, 108 | // and it must not use the iterator once the GargantuanTable object 109 | // on which the iterator was created has been deleted. 110 | // 111 | // The iterator is initially positioned at the beginning of the table. 112 | // 113 | // This method is equivalent to: 114 | // Iterator* iter = table->NewIterator(); 115 | // iter->Seek(""); 116 | // return iter; 117 | // If you are going to immediately seek to another place in the 118 | // returned iterator, it will be faster to use NewIterator() 119 | // and avoid the extra seek. 120 | Iterator* GetIterator() const; 121 | 122 | 但也要避免罗罗嗦嗦, 或者对显而易见的内容进行说明. 下面的注释就没有必要加上 "否则返回 false", 因为已经暗含其中了: 123 | 124 | .. code-block:: c++ 125 | 126 | // Returns true if the table cannot hold any more entries. 127 | bool IsTableFull(); 128 | 129 | 注释函数重载时, 注释的重点应该是函数中被重载的部分, 而不是简单的重复被重载的函数的注释. 多数情况下, 函数重载不需要额外的文档, 因此也没有必要加上注释. 130 | 131 | 注释构造/析构函数时, 切记读代码的人知道构造/析构函数的功能, 所以 "销毁这一对象" 这样的注释是没有意义的. 你应当注明的是注明构造函数对参数做了什么 (例如, 是否取得指针所有权) 以及析构函数清理了什么. 如果都是些无关紧要的内容, 直接省掉注释. 析构函数前没有注释是很正常的. 132 | 133 | 函数定义 134 | ============================= 135 | 136 | 如果函数的实现过程中用到了很巧妙的方式, 那么在函数定义处应当加上解释性的注释. 例如, 你所使用的编程技巧, 实现的大致步骤, 或解释如此实现的理由. 举个例子, 你可以说明为什么函数的前半部分要加锁而后半部分不需要. 137 | 138 | *不要* 从 ``.h`` 文件或其他地方的函数声明处直接复制注释. 简要重述函数功能是可以的, 但注释重点要放在如何实现上. 139 | 140 | 8.5. 变量注释 141 | ~~~~~~~~~~~~~~~~~~~~~~ 142 | 143 | **总述** 144 | 145 | 通常变量名本身足以很好说明变量用途. 某些情况下, 也需要额外的注释说明. 146 | 147 | **说明** 148 | 149 | 类数据成员 150 | ============================= 151 | 152 | 每个类数据成员 (也叫实例变量或成员变量) 都应该用注释说明用途. 如果有非变量的参数(例如特殊值, 数据成员之间的关系, 生命周期等)不能够用类型与变量名明确表达, 则应当加上注释. 然而, 如果变量类型与变量名已经足以描述一个变量, 那么就不再需要加上注释. 153 | 154 | 特别地, 如果变量可以接受 ``NULL`` 或 ``-1`` 等警戒值, 须加以说明. 比如: 155 | 156 | .. code-block:: c++ 157 | 158 | private: 159 | // Used to bounds-check table accesses. -1 means 160 | // that we don't yet know how many entries the table has. 161 | int num_total_entries_; 162 | 163 | 164 | 全局变量 165 | ============================= 166 | 167 | 和数据成员一样, 所有全局变量也要注释说明含义及用途, 以及作为全局变量的原因. 比如: 168 | 169 | .. code-block:: c++ 170 | 171 | // The total number of tests cases that we run through in this regression test. 172 | const int kNumTestCases = 6; 173 | 174 | 8.6. 实现注释 175 | ~~~~~~~~~~~~~~~~~~~~~~ 176 | 177 | **总述** 178 | 179 | 对于代码中巧妙的, 晦涩的, 有趣的, 重要的地方加以注释. 180 | 181 | **说明** 182 | 183 | 代码前注释 184 | ============================= 185 | 186 | 巧妙或复杂的代码段前要加注释. 比如: 187 | 188 | .. code-block:: c++ 189 | 190 | // Divide result by two, taking into account that x 191 | // contains the carry from the add. 192 | for (int i = 0; i < result->size(); i++) { 193 | x = (x << 8) + (*result)[i]; 194 | (*result)[i] = x >> 1; 195 | x &= 1; 196 | } 197 | 198 | 行注释 199 | ============================= 200 | 201 | 比较隐晦的地方要在行尾加入注释. 在行尾空两格进行注释. 比如: 202 | 203 | .. code-block:: c++ 204 | 205 | // If we have enough memory, mmap the data portion too. 206 | mmap_budget = max(0, mmap_budget - index_->length()); 207 | if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytes, mlock)) 208 | return; // Error already logged. 209 | 210 | 注意, 这里用了两段注释分别描述这段代码的作用, 和提示函数返回时错误已经被记入日志. 211 | 212 | 如果你需要连续进行多行注释, 可以使之对齐获得更好的可读性: 213 | 214 | .. code-block:: c++ 215 | 216 | DoSomething(); // Comment here so the comments line up. 217 | DoSomethingElseThatIsLonger(); // Two spaces between the code and the comment. 218 | { // One space before comment when opening a new scope is allowed, 219 | // thus the comment lines up with the following comments and code. 220 | DoSomethingElse(); // Two spaces before line comments normally. 221 | } 222 | std::vector list{ 223 | // Comments in braced lists describe the next element... 224 | "First item", 225 | // .. and should be aligned appropriately. 226 | "Second item"}; 227 | DoSomething(); /* For trailing block comments, one space is fine. */ 228 | 229 | 函数参数注释 230 | ============================= 231 | 232 | 如果函数参数的意义不明显, 考虑用下面的方式进行弥补: 233 | 234 | - 如果参数是一个字面常量, 并且这一常量在多处函数调用中被使用, 用以推断它们一致, 你应当用一个常量名让这一约定变得更明显, 并且保证这一约定不会被打破. 235 | 236 | - 考虑更改函数的签名, 让某个 ``bool`` 类型的参数变为 ``enum`` 类型, 这样可以让这个参数的值表达其意义. 237 | 238 | - 如果某个函数有多个配置选项, 你可以考虑定义一个类或结构体以保存所有的选项, 并传入类或结构体的实例. 这样的方法有许多优点, 例如这样的选项可以在调用处用变量名引用, 这样就能清晰地表明其意义. 同时也减少了函数参数的数量, 使得函数调用更易读也易写. 除此之外, 以这样的方式, 如果你使用其他的选项, 就无需对调用点进行更改. 239 | 240 | - 用具名变量代替大段而复杂的嵌套表达式. 241 | 242 | - 万不得已时, 才考虑在调用点用注释阐明参数的意义. 243 | 244 | 比如下面的示例的对比: 245 | 246 | .. code-block:: c++ 247 | 248 | // What are these arguments? 249 | const DecimalNumber product = CalculateProduct(values, 7, false, nullptr); 250 | 251 | 和 252 | 253 | .. code-block:: c++ 254 | 255 | ProductOptions options; 256 | options.set_precision_decimals(7); 257 | options.set_use_cache(ProductOptions::kDontUseCache); 258 | const DecimalNumber product = 259 | CalculateProduct(values, options, /*completion_callback=*/nullptr); 260 | 261 | 哪个更清晰一目了然. 262 | 263 | 不允许的行为 264 | ============================= 265 | 266 | 不要描述显而易见的现象, *永远不要* 用自然语言翻译代码作为注释, 除非即使对深入理解 C++ 的读者来说代码的行为都是不明显的. 要假设读代码的人 C++ 水平比你高, 即便他/她可能不知道你的用意: 267 | 268 | 你所提供的注释应当解释代码 *为什么* 要这么做和代码的目的, 或者最好是让代码自文档化. 269 | 270 | 比较这样的注释: 271 | 272 | .. code-block:: c++ 273 | 274 | // Find the element in the vector. <-- 差: 这太明显了! 275 | auto iter = std::find(v.begin(), v.end(), element); 276 | if (iter != v.end()) { 277 | Process(element); 278 | } 279 | 280 | 和这样的注释: 281 | 282 | .. code-block:: c++ 283 | 284 | // Process "element" unless it was already processed. 285 | auto iter = std::find(v.begin(), v.end(), element); 286 | if (iter != v.end()) { 287 | Process(element); 288 | } 289 | 290 | 自文档化的代码根本就不需要注释. 上面例子中的注释对下面的代码来说就是毫无必要的: 291 | 292 | .. code-block:: c++ 293 | 294 | if (!IsAlreadyProcessed(element)) { 295 | Process(element); 296 | } 297 | 298 | 8.7. 标点, 拼写和语法 299 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 300 | 301 | **总述** 302 | 303 | 注意标点, 拼写和语法; 写的好的注释比差的要易读的多. 304 | 305 | **说明** 306 | 307 | 注释的通常写法是包含正确大小写和结尾句号的完整叙述性语句. 大多数情况下, 完整的句子比句子片段可读性更高. 短一点的注释, 比如代码行尾注释, 可以随意点, 但依然要注意风格的一致性. 308 | 309 | 虽然被别人指出该用分号时却用了逗号多少有些尴尬, 但清晰易读的代码还是很重要的. 正确的标点, 拼写和语法对此会有很大帮助. 310 | 311 | 8.8. TODO 注释 312 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 313 | 314 | **总述** 315 | 316 | 对那些临时的, 短期的解决方案, 或已经够好但仍不完美的代码使用 ``TODO`` 注释. 317 | 318 | ``TODO`` 注释要使用全大写的字符串 ``TODO``, 在随后的圆括号里写上你的名字, 邮件地址, bug ID, 或其它身份标识和与这一 ``TODO`` 相关的 issue. 主要目的是让添加注释的人 (也是可以请求提供更多细节的人) 可根据规范的 ``TODO`` 格式进行查找. 添加 ``TODO`` 注释并不意味着你要自己来修正, 因此当你加上带有姓名的 ``TODO`` 时, 一般都是写上自己的名字. 319 | 320 | .. code-block:: c++ 321 | 322 | // TODO(kl@gmail.com): Use a "*" here for concatenation operator. 323 | // TODO(Zeke) change this to use relations. 324 | // TODO(bug 12345): remove the "Last visitors" feature 325 | 326 | 如果加 ``TODO`` 是为了在 "将来某一天做某事", 可以附上一个非常明确的时间 "Fix by November 2005"), 或者一个明确的事项 ("Remove this code when all clients can handle XML responses."). 327 | 328 | 8.9. 弃用注释 329 | ~~~~~~~~~~~~~~~~~~~~~~ 330 | 331 | **总述** 332 | 333 | 通过弃用注释(``DEPRECATED`` comments)以标记某接口点已弃用. 334 | 335 | 您可以写上包含全大写的 ``DEPRECATED`` 的注释, 以标记某接口为弃用状态. 注释可以放在接口声明前, 或者同一行. 336 | 337 | 在 ``DEPRECATED`` 一词后, 在括号中留下您的名字, 邮箱地址以及其他身份标识. 338 | 339 | 弃用注释应当包涵简短而清晰的指引, 以帮助其他人修复其调用点. 在 C++ 中, 你可以将一个弃用函数改造成一个内联函数, 这一函数将调用新的接口. 340 | 341 | 仅仅标记接口为 ``DEPRECATED`` 并不会让大家不约而同地弃用, 您还得亲自主动修正调用点(callsites), 或是找个帮手. 342 | 343 | 修正好的代码应该不会再涉及弃用接口点了, 着实改用新接口点. 如果您不知从何下手, 可以找标记弃用注释的当事人一起商量. 344 | 345 | 译者 (YuleFox) 笔记 346 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 347 | 348 | #. 关于注释风格, 很多 C++ 的 coders 更喜欢行注释, C coders 或许对块注释依然情有独钟, 或者在文件头大段大段的注释时使用块注释; 349 | #. 文件注释可以炫耀你的成就, 也是为了捅了篓子别人可以找你; 350 | #. 注释要言简意赅, 不要拖沓冗余, 复杂的东西简单化和简单的东西复杂化都是要被鄙视的; 351 | #. 对于 Chinese coders 来说, 用英文注释还是用中文注释, it is a problem, 但不管怎样, 注释是为了让别人看懂, 难道是为了炫耀编程语言之外的你的母语或外语水平吗; 352 | #. 注释不要太乱, 适当的缩进才会让人乐意看. 但也没有必要规定注释从第几列开始 (我自己写代码的时候总喜欢这样), UNIX/LINUX 下还可以约定是使用 tab 还是 space, 个人倾向于 space; 353 | #. TODO 很不错, 有时候, 注释确实是为了标记一些未完成的或完成的不尽如人意的地方, 这样一搜索, 就知道还有哪些活要干, 日志都省了. 354 | -------------------------------------------------------------------------------- /google-typescript-styleguide/syntax.rst: -------------------------------------------------------------------------------- 1 | 语法规范 2 | ################################################################################ 3 | 4 | .. _ts-identifiers: 5 | 6 | 标识符 7 | ******************************************************************************** 8 | 9 | .. _ts-naming: 10 | 11 | 命名规范 12 | ================================================================================ 13 | 14 | 在 TypeScript 中,标识符只能使用 ASCII 码表中的字母、数字、下划线与 ``(``。因此,合法的标识符可以使用正则表达式 ``[\)\w]+`` 进行匹配。根据标识符的用途不同,使用的命名法也不同,如下表所示: 15 | 16 | ======================================== ======================================== 17 | 命名法 分类 18 | ======================================== ======================================== 19 | 帕斯卡命名法( ``UpperCamelCase`` ) 类、接口、类型、枚举、装饰器、类型参数 20 | 驼峰式命名法( ``lowerCamelCase`` ) 变量、参数、函数、方法、属性、模块别名 21 | 全大写下划线命名法( ``CONSTANT_CASE``) 全局常量、枚举值 22 | 私有成员命名法( ``#ident`` ) 不允许使用 23 | ======================================== ======================================== 24 | 25 | .. _ts-abbreviations: 26 | 27 | 缩写 28 | -------------------------------------------------------------------------------- 29 | 30 | 缩写应被视为一个词。例如,应使用 ``loadHttpUrl``,而非 ``loadHTTPURL``。平台有特殊要求的标识符例外,如 ``XMLHttpRequest``。 31 | 32 | .. _ts-dollar-sign: 33 | 34 | 美元符号 \$ 35 | -------------------------------------------------------------------------------- 36 | 37 | 一般情况下,标识符不应使用 `$`,除非为了与第三方框架的命名规范保持一致。关于 `$` 的使用,可参见 :ref:`ts-naming-style` 一节对 ``Observable`` 类型的说明。 38 | 39 | .. _ts-type-parameters: 40 | 41 | 类型参数 42 | -------------------------------------------------------------------------------- 43 | 44 | 形如 ``Array`` 的类型参数既可以使用单个大写字母(如 ``T``),也可以使用帕斯卡命名法(如 ``UpperCamelCase``)。 45 | 46 | .. _ts-test-names: 47 | 48 | 测试用例 49 | -------------------------------------------------------------------------------- 50 | 51 | 无论是在 `Closure `_ 库的 ``testSuites`` 还是 `xUnit `_ 风格的测试框架中,都可以使用 ``_`` 作为标识符的分隔符,例如 ``testX_whenY_doesZ()``。 52 | 53 | .. _ts-underscore-prefix-suffix: 54 | 55 | ``_`` 前缀与后缀 56 | -------------------------------------------------------------------------------- 57 | 58 | 标识符禁止使用下划线 ``_`` 作为前缀或后缀。这也意味着,禁止使用单个下划线 ``_`` 作为标识符(例如:用来表示未被使用的参数)。 59 | 60 | 如果需要从数组或元组中取出某个或某几个特定的元素的话,可以在解构语句中插入额外的逗号,忽略掉不需要的元素: 61 | 62 | .. code-block:: typescript 63 | 64 | const [a, , b] = [1, 5, 10]; // a <- 1, b <- 10 65 | 66 | .. _ts-imports: 67 | 68 | 导入模块 69 | -------------------------------------------------------------------------------- 70 | 71 | 导入模块的命名空间时使用驼峰命名法(``lowerCamelCase``),文件名则使用蛇形命名法(``snake_case``)。例如: 72 | 73 | .. code-block:: typescript 74 | 75 | import * as fooBar from './foo_bar'; 76 | 77 | 一些库可能会在导入命名空间时使用某种特定的前缀,这与这里规定的命名规范有所冲突。然而,由于其中的一些库已经被广泛使用,因此遵循它们的特殊规则反而能够获得更好的可读性。这些特例包括: 78 | 79 | * `jQuery `_,使用 ``$`` 前缀。 80 | 81 | * `three.js `_,使用 ``THREE`` 前缀。 82 | 83 | .. _ts-constants: 84 | 85 | 常量 86 | -------------------------------------------------------------------------------- 87 | 88 | 常量命名(``CONSTANT_CASE``)表示某个值不可被修改。它还可以用于虽然技术上可以实现,但是用户不应当试图修改的值,比如并未进行深度冻结(deep frozen)的值。 89 | 90 | .. code-block:: typescript 91 | 92 | const UNIT_SUFFIXES = { 93 | 'milliseconds': 'ms', 94 | 'seconds': 's', 95 | }; 96 | // UNIT_SUFFIXES 使用了常量命名, 97 | // 这意味着用户不应试图修改它, 98 | // 即使它实际上是一个可变的值。 99 | 100 | 这里所说的常量,也包括类中的静态只读属性: 101 | 102 | .. code-block:: typescript 103 | 104 | class Foo { 105 | private static readonly MY_SPECIAL_NUMBER = 5; 106 | 107 | bar() { 108 | return 2 * Foo.MY_SPECIAL_NUMBER; 109 | } 110 | } 111 | 112 | .. _ts-others: 113 | 114 | 其他 115 | -------------------------------------------------------------------------------- 116 | 117 | 如果某个值在程序的整个运行生命周期中会被多次实例化或被用户以任何方式进行修改,则它必须使用驼峰式命名法。 118 | 119 | 如果某个值是作为某个接口的实现的箭头函数,则它也可以使用驼峰式命名法。 120 | 121 | .. _ts-aliases: 122 | 123 | 别名 124 | ================================================================================ 125 | 126 | 在为一个已有的标识符创建具有局部作用域的别名时,别名的命名方式应当与现有的标识符和现有的命名规范保持一致。声明别名时,应使用 ``const`` (如果它是一个变量)或 ``readonly`` (如果它是类里的一个字段)。 127 | 128 | .. code-block:: typescript 129 | 130 | const {Foo} = SomeType; 131 | const CAPACITY = 5; 132 | 133 | class Teapot { 134 | readonly BrewStateEnum = BrewStateEnum; 135 | readonly CAPACITY = CAPACITY; 136 | } 137 | 138 | .. _ts-naming-style: 139 | 140 | 命名风格 141 | ================================================================================ 142 | 143 | TypeScript 中的类型表达了丰富的信息,因此在起名时不应与类型中所携带的信息重复。(关于更多在起名时应避免的内容,可参见谷歌的 `Testing Blog `_。) 144 | 145 | 这里有几个具体的例子: 146 | 147 | * 不要为私有属性或方法名添加下划线 `_` 前缀或后缀。 148 | 149 | * 不要为可选参数添加 `opt_` 前缀。 150 | 151 | * 关于在存取器中的特例,参见后文 :ref:`name-and-order-of-includes` 。 152 | 153 | * 除非在项目中已成惯例,否则不要显式地标记接口类型(例如不要使用 ``IMyInterface`` 或者 ``MyFooInterface`` )。在为类添加接口时,接口名称中应包含创建这一接口的原因。(例如,在为类 ``TodoItem`` 创建一个将其转为 JSON 格式以用于存储或者序列化的接口时,可以将这一接口命名为 ``TodoItemStorage`` 。) 154 | 155 | * 对于 ``Observable`` 类型的值,通常的惯例是使用 ``$`` 前缀将其与一般类型的值进行区分,使之不致混淆。各个团队可以在与项目内部的现有做法保持一致的前提下,自行决定是否采用这一做法。 156 | 157 | .. _ts-descriptive-names: 158 | 159 | 描述性命名 160 | ================================================================================ 161 | 162 | 命名应当具有描述性且易于读者理解。不要使用对项目以外的用户而言含糊不清或并不熟悉的缩写,不要通过删减单词中的字母来强行创造缩写。 163 | 164 | 这一规则的例外是,对不超过十行的作用域中的变量,以及内部 API 的参数,可以使用短变量名(例如 ``i`` 、 ``j`` 等只有单个字母的变量名)。 165 | 166 | .. _ts-file-encoding: 167 | 168 | 文件编码 169 | ******************************************************************************** 170 | 171 | 使用 UTF-8 文件编码。 172 | 173 | 对于非 ASCII 字符,应使用实际的 Unicode 字符(例如 ``∞`` )。对于非输出字符,使用对应的十六进制编码或 Unicode 转义编码(如 ``\u221e`` ),并添加注释进行说明。 174 | 175 | .. code-block:: typescript 176 | 177 | // 应当这样做!即使没有注释也十分易懂。 178 | const units = 'μs'; 179 | 180 | // 应当这样做!对非输出字符进行转义。 181 | const output = '\ufeff' + content; // 字节顺序标记(Byte Order Mark,BOM) 182 | 183 | .. code-block:: typescript 184 | 185 | // 不要这样做!即使加上注释也不太好读,而且容易出错。 186 | const units = '\u03bcs'; // Greek letter mu, 's' 187 | 188 | // 不要省略注释!读者在缺少注释的情况下很难理解这个字符的含义。 189 | const output = '\ufeff' + content; 190 | 191 | .. _ts-comments-documentation: 192 | 193 | 注释与文档 194 | ******************************************************************************** 195 | 196 | .. _ts-jsdoc-vs-comments: 197 | 198 | 用 JSDoc 还是 注释? 199 | ================================================================================ 200 | 201 | TypesScript 中有两种类型的注释:JSDoc ``/** ... */`` 和普通注释 ``// ... 或者 /* ... */`` 。 202 | 203 | * 对于文档,也就是用户应当阅读的注释,使用 ``/** JSDoc */`` 。 204 | * 对于实现说明,也就是只和代码本身的实现细节有关的注释,使用 ``// 行注释`` 。 205 | 206 | JSDoc 注释能够为工具(例如编辑器或文档生成器)所识别,而普通注释只能供人阅读。 207 | 208 | .. _ts-jsdoc-rules-follow-the-js-style: 209 | 210 | JSDoc 规范 211 | ================================================================================ 212 | 213 | JSDoc 的规范大部分遵循 JavaScript 风格指南中的规定。具体地说,遵循 JavaScript 风格指南中 :ref:`js-comments` 一节的规则。本节的剩余部分只对与这些规则不一致的部分进行说明。 214 | 215 | .. _ts-document-all-top-level-exports-of-modules: 216 | 217 | 对所有导出的顶层模块进行注释 218 | ================================================================================ 219 | 220 | 使用 ``/** JSDoc */`` 注释为代码的用户提供信息。这些注释应当言之有物,切忌仅仅将属性名或参数名重抄一遍。如果代码的审核人认为某个属性或方法的作用不能从它的名字上一目了然地看出来的话,这些属性和方法同样应当使用 ``/** JSDoc */`` 注释添加说明文档,无论它们是否被导出,是公开还是私有的。 221 | 222 | .. _ts-omit-comments-that-are-redundant-with-ts: 223 | 224 | 省略对于 TypeScript 而言多余的注释 225 | ================================================================================ 226 | 227 | 例如,不要在 ``@param`` 或 ``@return`` 注释中声明类型,不要在使用了 ``implements`` 、 ``enum`` 、 ``private`` 等关键字的地方添加 ``@implements`` 、 ``@enum`` 、 ``@private`` 等注释。 228 | 229 | .. _ts-do-not-use-override: 230 | 231 | 不要使用 ``@override`` 232 | ================================================================================ 233 | 234 | 不要在 TypeScript 代码中使用 ``@override`` 注释。 ``@override`` 并不会被编译器视为强制性约束,这会导致注释与实现上的不一致性。如果纯粹为了文档添加这一注释,反而令人困惑。 235 | 236 | .. _ts-make-comments-that-actually-add-information: 237 | 238 | 注释必须言之有物 239 | ================================================================================ 240 | 241 | 虽然大多数情况下文档对代码十分有益,但对于那些并不用于导出的符号,有时其函数或参数的名称与类型便足以描述自身了。 242 | 243 | 注释切忌照抄参数类型和参数名,如下面的反面示例: 244 | 245 | .. code-block:: typescript 246 | 247 | // 不要这样做!这个注释没有任何有意义的内容。 248 | /** @param fooBarService Foo 应用的 Bar 服务 */ 249 | 250 | 因此,只有当需要添加额外信息时才使用 ``@param`` 和 ``@return`` 注释,其它情况下直接省略即可。 251 | 252 | .. code-block:: typescript 253 | 254 | /** 255 | * 发送 POST 请求,开始煮咖啡 256 | * @param amountLitres 煮咖啡的量,注意和煮锅的尺寸对应! 257 | */ 258 | brew(amountLitres: number, logger: Logger) { 259 | // ... 260 | } 261 | 262 | .. _ts-parameter-property-comments: 263 | 264 | 参数属性注释 265 | ================================================================================ 266 | 267 | 通过为构造函数的参数添加访问限定符,参数属性同时创建了构造函数参数和类成员。例如,如下的构造函数 268 | 269 | .. code-block:: typescript 270 | 271 | class Foo { 272 | constructor(private readonly bar: Bar) { } 273 | } 274 | 275 | 为 ``Foo`` 类创建了 ``Bar`` 类型的成员 ``bar`` 。 276 | 277 | 如果要为这些成员添加文档,应使用 JSDoc 的 ``@param`` 注释,这样编辑器会在调用构造函数和访问属性时显示对应的文档描述信息。 278 | 279 | .. code-block:: typescript 280 | 281 | /** 这个类演示了如何为参数属性添加文档 */ 282 | class ParamProps { 283 | /** 284 | * @param percolator 煮咖啡所用的咖啡壶。 285 | * @param beans 煮咖啡所用的咖啡豆。 286 | */ 287 | constructor( 288 | private readonly percolator: Percolator, 289 | private readonly beans: CoffeeBean[]) {} 290 | } 291 | 292 | 293 | .. code-block:: typescript 294 | 295 | /** 这个类演示了如何为普通成员添加文档 */ 296 | class OrdinaryClass { 297 | /** 下次调用 brew() 时所用的咖啡豆。 */ 298 | nextBean: CoffeeBean; 299 | 300 | constructor(initialBean: CoffeeBean) { 301 | this.nextBean = initialBean; 302 | } 303 | } 304 | 305 | .. _ts-comments-when-calling-a-function: 306 | 307 | 函数调用注释 308 | ================================================================================ 309 | 310 | 如果有需要,可以在函数的调用点使用行内的 ``/* 块注释 */`` 为参数添加文档,或者使用字面量对象为参数添加名称并在函数声明中进行解构。注释的格式和位置没有明确的规定。 311 | 312 | .. code-block:: typescript 313 | 314 | // 使用行内块注释为难以理解的参数添加说明: 315 | new Percolator().brew(/* amountLitres= */ 5); 316 | 317 | // 或者使用字面量对象为参数命名,并在函数 brew 的声明中将参数解构: 318 | new Percolator().brew({amountLitres: 5}); 319 | 320 | 321 | .. code-block:: typescript 322 | 323 | /** 一个古老的咖啡壶 {@link CoffeeBrewer} */ 324 | export class Percolator implements CoffeeBrewer { 325 | /** 326 | * 煮咖啡。 327 | * @param amountLitres 煮咖啡的量,注意必须和煮锅的尺寸对应! 328 | */ 329 | brew(amountLitres: number) { 330 | // 这个实现煮出来的咖啡味道差极了,不管了。 331 | // TODO(b/12345): 优化煮咖啡的过程。 332 | } 333 | } 334 | 335 | .. _ts-place-documentation-prior-to-decorators: 336 | 337 | 将文档置于装饰器之前 338 | ================================================================================ 339 | 340 | 文档、方法或者属性如果同时具有装饰器(例如 ``@Component``)和 JSDoc 注释,应当将 JSDoc 置于装饰器之前。 341 | 342 | 禁止将 JSDoc 置于装饰器和被装饰的对象之间。 343 | 344 | .. code-block:: typescript 345 | 346 | // 不要这样做!JSDoc 被放在装饰器 @Component 和类 FooComponent 中间了! 347 | @Component({ 348 | selector: 'foo', 349 | template: 'bar', 350 | }) 351 | /** 打印 "bar" 的组件。 */ 352 | export class FooComponent {} 353 | 354 | 应当将 JSDoc 置于装饰器之前。 355 | 356 | .. code-block:: typescript 357 | 358 | /** 打印 "bar" 的组件。 */ 359 | @Component({ 360 | selector: 'foo', 361 | template: 'bar', 362 | }) 363 | export class FooComponent {} 364 | -------------------------------------------------------------------------------- /google-cpp-styleguide/classes.rst: -------------------------------------------------------------------------------- 1 | 3. 类 2 | ------------------------ 3 | 4 | 类是 C++ 中代码的基本单元. 显然, 它们被广泛使用. 本节列举了在写一个类时的主要注意事项. 5 | 6 | 3.1. 构造函数的职责 7 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | **总述** 10 | 11 | 不要在构造函数中调用虚函数, 也不要在无法报出错误时进行可能失败的初始化. 12 | 13 | **定义** 14 | 15 | 在构造函数中可以进行各种初始化操作. 16 | 17 | **优点** 18 | 19 | - 无需考虑类是否被初始化. 20 | 21 | - 经过构造函数完全初始化后的对象可以为 ``const`` 类型, 也能更方便地被标准容器或算法使用. 22 | 23 | **缺点** 24 | 25 | - 如果在构造函数内调用了自身的虚函数, 这类调用是不会重定向到子类的虚函数实现. 即使当前没有子类化实现, 将来仍是隐患. 26 | 27 | - 在没有使程序崩溃 (因为并不是一个始终合适的方法) 或者使用异常 (因为已经被 :ref:`禁用 ` 了) 等方法的条件下, 构造函数很难上报错误 28 | 29 | - 如果执行失败, 会得到一个初始化失败的对象, 这个对象有可能进入不正常的状态, 必须使用 ``bool IsValid()`` 或类似这样的机制才能检查出来, 然而这是一个十分容易被疏忽的方法. 30 | 31 | - 构造函数的地址是无法被取得的, 因此, 举例来说, 由构造函数完成的工作是无法以简单的方式交给其他线程的. 32 | 33 | **结论** 34 | 35 | 构造函数不允许调用虚函数. 如果代码允许, 直接终止程序是一个合适的处理错误的方式. 否则, 考虑用 ``Init()`` 方法或工厂函数. 36 | 37 | 构造函数不得调用虚函数, 或尝试报告一个非致命错误. 如果对象需要进行有意义的 (non-trivial) 初始化, 考虑使用明确的 Init() 方法或使用工厂模式. Avoid ``Init()`` methods on objects with no other states that affect which public methods may be called (此类形式的半构造对象有时无法正确工作). 38 | 39 | .. _implicit-conversions: 40 | 41 | 3.2. 隐式类型转换 42 | ~~~~~~~~~~~~~~~~~~~~ 43 | 44 | **总述** 45 | 46 | 不要定义隐式类型转换. 对于转换运算符和单参数构造函数, 请使用 ``explicit`` 关键字. 47 | 48 | **定义** 49 | 50 | 隐式类型转换允许一个某种类型 (称作 *源类型*) 的对象被用于需要另一种类型 (称作 *目的类型*) 的位置, 例如, 将一个 ``int`` 类型的参数传递给需要 ``double`` 类型的函数. 51 | 52 | 除了语言所定义的隐式类型转换, 用户还可以通过在类定义中添加合适的成员定义自己需要的转换. 在源类型中定义隐式类型转换, 可以通过目的类型名的类型转换运算符实现 (例如 ``operator bool()``). 在目的类型中定义隐式类型转换, 则通过以源类型作为其唯一参数 (或唯一无默认值的参数) 的构造函数实现. 53 | 54 | ``explicit`` 关键字可以用于构造函数或 (在 C++11 引入) 类型转换运算符, 以保证只有当目的类型在调用点被显式写明时才能进行类型转换, 例如使用 ``cast``. 这不仅作用于隐式类型转换, 还能作用于 C++11 的列表初始化语法: 55 | 56 | .. code-block:: c++ 57 | 58 | class Foo { 59 | explicit Foo(int x, double y); 60 | ... 61 | }; 62 | 63 | void Func(Foo f); 64 | 65 | 此时下面的代码是不允许的: 66 | 67 | .. code-block:: c++ 68 | 69 | Func({42, 3.14}); // Error 70 | 71 | 这一代码从技术上说并非隐式类型转换, 但是语言标准认为这是 ``explicit`` 应当限制的行为. 72 | 73 | **优点** 74 | 75 | - 有时目的类型名是一目了然的, 通过避免显式地写出类型名, 隐式类型转换可以让一个类型的可用性和表达性更强. 76 | 77 | - 隐式类型转换可以简单地取代函数重载. 78 | 79 | - 在初始化对象时, 列表初始化语法是一种简洁明了的写法. 80 | 81 | **缺点** 82 | 83 | - 隐式类型转换会隐藏类型不匹配的错误. 有时, 目的类型并不符合用户的期望, 甚至用户根本没有意识到发生了类型转换. 84 | 85 | - 隐式类型转换会让代码难以阅读, 尤其是在有函数重载的时候, 因为这时很难判断到底是哪个函数被调用. 86 | 87 | - 单参数构造函数有可能会被无意地用作隐式类型转换. 88 | 89 | - 如果单参数构造函数没有加上 ``explicit`` 关键字, 读者无法判断这一函数究竟是要作为隐式类型转换, 还是作者忘了加上 ``explicit`` 标记. 90 | 91 | - 并没有明确的方法用来判断哪个类应该提供类型转换, 这会使得代码变得含糊不清. 92 | 93 | - 如果目的类型是隐式指定的, 那么列表初始化会出现和隐式类型转换一样的问题, 尤其是在列表中只有一个元素的时候. 94 | 95 | **结论** 96 | 97 | 在类型定义中, 类型转换运算符和单参数构造函数都应当用 ``explicit`` 进行标记. 一个例外是, 拷贝和移动构造函数不应当被标记为 ``explicit``, 因为它们并不执行类型转换. 对于设计目的就是用于对其他类型进行透明包装的类来说, 隐式类型转换有时是必要且合适的. 这时应当联系项目组长并说明特殊情况. 98 | 99 | 不能以一个参数进行调用的构造函数不应当加上 ``explicit``. 接受一个 ``std::initializer_list`` 作为参数的构造函数也应当省略 ``explicit``, 以便支持拷贝初始化 (例如 ``MyType m = {1, 2};``). 100 | 101 | .. _copyable-and-movable-types: 102 | 103 | 3.3. 可拷贝类型和可移动类型 104 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 105 | 106 | **总述** 107 | 108 | 如果你的类型需要, 就让它们支持拷贝 / 移动. 否则, 就把隐式产生的拷贝和移动函数禁用. 109 | 110 | **定义** 111 | 112 | 可拷贝类型允许对象在初始化时得到来自相同类型的另一对象的值, 或在赋值时被赋予相同类型的另一对象的值, 同时不改变源对象的值. 对于用户定义的类型, 拷贝操作一般通过拷贝构造函数与拷贝赋值操作符定义. ``string`` 类型就是一个可拷贝类型的例子. 113 | 114 | 可移动类型允许对象在初始化时得到来自相同类型的临时对象的值, 或在赋值时被赋予相同类型的临时对象的值 (因此所有可拷贝对象也是可移动的). ``std::unique_ptr`` 就是一个可移动但不可复制的对象的例子. 对于用户定义的类型, 移动操作一般是通过移动构造函数和移动赋值操作符实现的. 115 | 116 | 拷贝 / 移动构造函数在某些情况下会被编译器隐式调用. 例如, 通过传值的方式传递对象. 117 | 118 | **优点** 119 | 120 | 可移动及可拷贝类型的对象可以通过传值的方式进行传递或者返回, 这使得 API 更简单, 更安全也更通用. 与传指针和引用不同, 这样的传递不会造成所有权, 生命周期, 可变性等方面的混乱, 也就没必要在协议中予以明确. 这同时也防止了客户端与实现在非作用域内的交互, 使得它们更容易被理解与维护. 这样的对象可以和需要传值操作的通用 API 一起使用, 例如大多数容器. 121 | 122 | 拷贝 / 移动构造函数与赋值操作一般来说要比它们的各种替代方案, 比如 ``Clone()``, ``CopyFrom()`` or ``Swap()``, 更容易定义, 因为它们能通过编译器产生, 无论是隐式的还是通过 ``= default``. 这种方式很简洁, 也保证所有数据成员都会被复制. 拷贝与移动构造函数一般也更高效, 因为它们不需要堆的分配或者是单独的初始化和赋值步骤, 同时, 对于类似 `省略不必要的拷贝 `_ 这样的优化它们也更加合适. 123 | 124 | 移动操作允许隐式且高效地将源数据转移出右值对象. 这有时能让代码风格更加清晰. 125 | 126 | **缺点** 127 | 128 | 许多类型都不需要拷贝, 为它们提供拷贝操作会让人迷惑, 也显得荒谬而不合理. 单件类型 (``Registerer``), 与特定的作用域相关的类型 (``Cleanup``), 与其他对象实体紧耦合的类型 (``Mutex``) 从逻辑上来说都不应该提供拷贝操作. 为基类提供拷贝 / 赋值操作是有害的, 因为在使用它们时会造成 `对象切割 `_ . 默认的或者随意的拷贝操作实现可能是不正确的, 这往往导致令人困惑并且难以诊断出的错误. 129 | 130 | 拷贝构造函数是隐式调用的, 也就是说, 这些调用很容易被忽略. 这会让人迷惑, 尤其是对那些所用的语言约定或强制要求传引用的程序员来说更是如此. 同时, 这从一定程度上说会鼓励过度拷贝, 从而导致性能上的问题. 131 | 132 | **结论** 133 | 134 | 如果需要就让你的类型可拷贝 / 可移动. 作为一个经验法则, 如果对于你的用户来说这个拷贝操作不是一眼就能看出来的, 那就不要把类型设置为可拷贝. 如果让类型可拷贝, 一定要同时给出拷贝构造函数和赋值操作的定义, 反之亦然. 如果让类型可移动, 同时移动操作的效率高于拷贝操作, 那么就把移动的两个操作 (移动构造函数和赋值操作) 也给出定义. 如果类型不可拷贝, 但是移动操作的正确性对用户显然可见, 那么把这个类型设置为只可移动并定义移动的两个操作. 135 | 136 | 如果定义了拷贝/移动操作, 则要保证这些操作的默认实现是正确的. 记得时刻检查默认操作的正确性, 并且在文档中说明类是可拷贝的且/或可移动的. 137 | 138 | .. code-block:: c++ 139 | 140 | class Foo { 141 | public: 142 | Foo(Foo&& other) : field_(other.field) {} 143 | // 差, 只定义了移动构造函数, 而没有定义对应的赋值运算符. 144 | 145 | private: 146 | Field field_; 147 | }; 148 | 149 | 由于存在对象切割的风险, 不要为任何有可能有派生类的对象提供赋值操作或者拷贝 / 移动构造函数 (当然也不要继承有这样的成员函数的类). 如果你的基类需要可复制属性, 请提供一个 ``public virtual Clone()`` 和一个 ``protected`` 的拷贝构造函数以供派生类实现. 150 | 151 | 如果你的类不需要拷贝 / 移动操作, 请显式地通过在 ``public`` 域中使用 ``= delete`` 或其他手段禁用之. 152 | 153 | .. code-block:: c++ 154 | 155 | // MyClass is neither copyable nor movable. 156 | MyClass(const MyClass&) = delete; 157 | MyClass& operator=(const MyClass&) = delete; 158 | 159 | .. _structs-vs-classes: 160 | 161 | 3.4. 结构体 VS. 类 162 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 163 | 164 | **总述** 165 | 166 | 仅当只有数据成员时使用 ``struct``, 其它一概使用 ``class``. 167 | 168 | **说明** 169 | 170 | 在 C++ 中 ``struct`` 和 ``class`` 关键字几乎含义一样. 我们为这两个关键字添加我们自己的语义理解, 以便为定义的数据类型选择合适的关键字. 171 | 172 | ``struct`` 用来定义包含数据的被动式对象, 也可以包含相关的常量, 但除了存取数据成员之外, 没有别的函数功能. 并且存取功能是通过直接访问位域, 而非函数调用. 除了构造函数, 析构函数, ``Initialize()``, ``Reset()``, ``Validate()`` 等类似的用于设定数据成员的函数外, 不能提供其它功能的函数. 173 | 174 | 如果需要更多的函数功能, ``class`` 更适合. 如果拿不准, 就用 ``class``. 175 | 176 | 为了和 STL 保持一致, 对于仿函数等特性可以不用 ``class`` 而是使用 ``struct``. 177 | 178 | 注意: 类和结构体的成员变量使用不同的 :ref:`命名规则 `. 179 | 180 | .. _inheritance: 181 | 182 | 3.5. 继承 183 | ~~~~~~~~~~~~~~~~~~~~ 184 | 185 | **总述** 186 | 187 | 使用组合 (YuleFox 注: 这一点也是 GoF 在 <> 里反复强调的) 常常比使用继承更合理. 如果使用继承的话, 定义为 ``public`` 继承. 188 | 189 | **定义** 190 | 191 | 当子类继承基类时, 子类包含了父基类所有数据及操作的定义. C++ 实践中, 继承主要用于两种场合: 实现继承, 子类继承父类的实现代码; :ref:`接口继承 `, 子类仅继承父类的方法名称. 192 | 193 | **优点** 194 | 195 | 实现继承通过原封不动的复用基类代码减少了代码量. 由于继承是在编译时声明, 程序员和编译器都可以理解相应操作并发现错误. 从编程角度而言, 接口继承是用来强制类输出特定的 API. 在类没有实现 API 中某个必须的方法时, 编译器同样会发现并报告错误. 196 | 197 | **缺点** 198 | 199 | 对于实现继承, 由于子类的实现代码散布在父类和子类间之间, 要理解其实现变得更加困难. 子类不能重写父类的非虚函数, 当然也就不能修改其实现. 基类也可能定义了一些数据成员, 因此还必须区分基类的实际布局. 200 | 201 | **结论** 202 | 203 | 所有继承必须是 ``public`` 的. 如果你想使用私有继承, 你应该替换成把基类的实例作为成员对象的方式. 204 | 205 | 不要过度使用实现继承. 组合常常更合适一些. 尽量做到只在 "是一个" ("is-a", YuleFox 注: 其他 "has-a" 情况下请使用组合) 的情况下使用继承: 如果 ``Bar`` 的确 "是一种" ``Foo``, ``Bar`` 才能继承 ``Foo``. 206 | 207 | 必要的话, 析构函数声明为 ``virtual``. 如果你的类有虚函数, 则析构函数也应该为虚函数. 208 | 209 | 对于可能被子类访问的成员函数, 不要过度使用 ``protected`` 关键字. 注意, 数据成员都必须是 :ref:`私有的 `. 210 | 211 | 对于重载的虚函数或虚析构函数, 使用 ``override``, 或 (较不常用的) ``final`` 关键字显式地进行标记. 较早 (早于 C++11) 的代码可能会使用 ``virtual`` 关键字作为不得已的选项. 因此, 在声明重载时, 请使用 ``override``, ``final`` 或 ``virtual`` 的其中之一进行标记. 标记为 ``override`` 或 ``final`` 的析构函数如果不是对基类虚函数的重载的话, 编译会报错, 这有助于捕获常见的错误. 这些标记起到了文档的作用, 因为如果省略这些关键字, 代码阅读者不得不检查所有父类, 以判断该函数是否是虚函数. 212 | 213 | .. _multiple-inheritance: 214 | 215 | 3.6. 多重继承 216 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 217 | 218 | **总述** 219 | 220 | 真正需要用到多重实现继承的情况少之又少. 只在以下情况我们才允许多重继承: 最多只有一个基类是非抽象类; 其它基类都是以 ``Interface`` 为后缀的 :ref:`纯接口类 `. 221 | 222 | **定义** 223 | 224 | 多重继承允许子类拥有多个基类. 要将作为 *纯接口* 的基类和具有 *实现* 的基类区别开来. 225 | 226 | **优点** 227 | 228 | 相比单继承 (见 :ref:`继承 `), 多重实现继承可以复用更多的代码. 229 | 230 | **缺点** 231 | 232 | 真正需要用到多重 *实现* 继承的情况少之又少. 有时多重实现继承看上去是不错的解决方案, 但这时你通常也可以找到一个更明确, 更清晰的不同解决方案. 233 | 234 | **结论** 235 | 236 | 只有当所有父类除第一个外都是 :ref:`纯接口类 ` 时, 才允许使用多重继承. 为确保它们是纯接口, 这些类必须以 ``Interface`` 为后缀. 237 | 238 | **注意** 239 | 240 | 关于该规则, Windows 下有个 :ref:`特例 `. 241 | 242 | .. _interface: 243 | 244 | 3.7. 接口 245 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 246 | 247 | **总述** 248 | 249 | 接口是指满足特定条件的类, 这些类以 ``Interface`` 为后缀 (不强制). 250 | 251 | **定义** 252 | 253 | 当一个类满足以下要求时, 称之为纯接口: 254 | 255 | - 只有纯虚函数 ("``=0``") 和静态函数 (除了下文提到的析构函数). 256 | 257 | - 没有非静态数据成员. 258 | 259 | - 没有定义任何构造函数. 如果有, 也不能带有参数, 并且必须为 ``protected``. 260 | 261 | - 如果它是一个子类, 也只能从满足上述条件并以 ``Interface`` 为后缀的类继承. 262 | 263 | 接口类不能被直接实例化, 因为它声明了纯虚函数. 为确保接口类的所有实现可被正确销毁, 必须为之声明虚析构函数 (作为上述第 1 条规则的特例, 析构函数不能是纯虚函数). 具体细节可参考 Stroustrup 的 *The C++ Programming Language, 3rd edition* 第 12.4 节. 264 | 265 | **优点** 266 | 267 | 以 ``Interface`` 为后缀可以提醒其他人不要为该接口类增加函数实现或非静态数据成员. 这一点对于 :ref:`多重继承 ` 尤其重要. 另外, 对于 Java 程序员来说, 接口的概念已是深入人心. 268 | 269 | **缺点** 270 | 271 | ``Interface`` 后缀增加了类名长度, 为阅读和理解带来不便. 同时, 接口属性作为实现细节不应暴露给用户. 272 | 273 | **结论** 274 | 275 | 只有在满足上述条件时, 类才以 ``Interface`` 结尾, 但反过来, 满足上述需要的类未必一定以 ``Interface`` 结尾. 276 | 277 | 3.8. 运算符重载 278 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 279 | 280 | **总述** 281 | 282 | 除少数特定环境外, 不要重载运算符. 也不要创建用户定义字面量. 283 | 284 | **定义** 285 | 286 | C++ 允许用户通过使用 ``operator`` 关键字 `对内建运算符进行重载定义 `_ , 只要其中一个参数是用户定义的类型. ``operator`` 关键字还允许用户使用 ``operator""`` 定义新的字面运算符, 并且定义类型转换函数, 例如 ``operator bool()``. 287 | 288 | **优点** 289 | 290 | 重载运算符可以让代码更简洁易懂, 也使得用户定义的类型和内建类型拥有相似的行为. 重载运算符对于某些运算来说是符合语言习惯的名称 (例如 ``==``, ``<``, ``=``, ``<<``), 遵循这些语言约定可以让用户定义的类型更易读, 也能更好地和需要这些重载运算符的函数库进行交互操作. 291 | 292 | 对于创建用户定义的类型的对象来说, 用户定义字面量是一种非常简洁的标记. 293 | 294 | **缺点** 295 | 296 | - 要提供正确, 一致, 不出现异常行为的操作符运算需要花费不少精力, 而且如果达不到这些要求的话, 会导致令人迷惑的 Bug. 297 | 298 | - 过度使用运算符会带来难以理解的代码, 尤其是在重载的操作符的语义与通常的约定不符合时. 299 | 300 | - 函数重载有多少弊端, 运算符重载就至少有多少. 301 | 302 | - 运算符重载会混淆视听, 让你误以为一些耗时的操作和操作内建类型一样轻巧. 303 | 304 | - 对重载运算符的调用点的查找需要的可就不仅仅是像 grep 那样的程序了, 这时需要能够理解 C++ 语法的搜索工具. 305 | 306 | - 如果重载运算符的参数写错, 此时得到的可能是一个完全不同的重载而非编译错误. 例如: ``foo < bar`` 执行的是一个行为, 而 ``&foo < &bar`` 执行的就是完全不同的另一个行为了. 307 | 308 | - 重载某些运算符本身就是有害的. 例如, 重载一元运算符 ``&`` 会导致同样的代码有完全不同的含义, 这取决于重载的声明对某段代码而言是否是可见的. 重载诸如 ``&&``, ``||`` 和 ``,`` 会导致运算顺序和内建运算的顺序不一致. 309 | 310 | - 运算符从通常定义在类的外部, 所以对于同一运算, 可能出现不同的文件引入了不同的定义的风险. 如果两种定义都链接到同一二进制文件, 就会导致未定义的行为, 有可能表现为难以发现的运行时错误. 311 | 312 | - 用户定义字面量所创建的语义形式对于某些有经验的 C++ 程序员来说都是很陌生的. 313 | 314 | **结论** 315 | 316 | 只有在意义明显, 不会出现奇怪的行为并且与对应的内建运算符的行为一致时才定义重载运算符. 例如, ``|`` 要作为位或或逻辑或来使用, 而不是作为 shell 中的管道. 317 | 318 | 只有对用户自己定义的类型重载运算符. 更准确地说, 将它们和它们所操作的类型定义在同一个头文件中, ``.cc`` 中和命名空间中. 这样做无论类型在哪里都能够使用定义的运算符, 并且最大程度上避免了多重定义的风险. 如果可能的话, 请避免将运算符定义为模板, 因为此时它们必须对任何模板参数都能够作用. 如果你定义了一个运算符, 请将其相关且有意义的运算符都进行定义, 并且保证这些定义的语义是一致的. 例如, 如果你重载了 ``<``, 那么请将所有的比较运算符都进行重载, 并且保证对于同一组参数, ``<`` 和 ``>`` 不会同时返回 ``true``. 319 | 320 | 建议不要将不进行修改的二元运算符定义为成员函数. 如果一个二元运算符被定义为类成员, 这时隐式转换会作用于右侧的参数却不会作用于左侧. 这时会出现 ``a < b`` 能够通过编译而 ``b < a`` 不能的情况, 这是很让人迷惑的. 321 | 322 | 不要为了避免重载操作符而走极端. 比如说, 应当定义 ``==``, ``=``, 和 ``<<`` 而不是 ``Equals()``, ``CopyFrom()`` 和 ``PrintTo()``. 反过来说, 不要只是为了满足函数库需要而去定义运算符重载. 比如说, 如果你的类型没有自然顺序, 而你要将它们存入 ``std::set`` 中, 最好还是定义一个自定义的比较运算符而不是重载 ``<``. 323 | 324 | 不要重载 ``&&``, ``||``, ``,`` 或一元运算符 ``&``. 不要重载 ``operator""``, 也就是说, 不要引入用户定义字面量. 325 | 326 | 类型转换运算符在 :ref:`隐式类型转换 ` 一节有提及. ``=`` 运算符在 :ref:`可拷贝类型和可移动类型 ` 一节有提及. 运算符 ``<<`` 在 :ref:`流 ` 一节有提及. 同时请参见 :ref:`函数重载 ` 一节, 其中提到的的规则对运算符重载同样适用. 327 | 328 | .. _access-control: 329 | 330 | 3.9. 存取控制 331 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 332 | 333 | **总述** 334 | 335 | 将 *所有* 数据成员声明为 ``private``, 除非是 ``static const`` 类型成员 (遵循 :ref:`常量命名规则 `). 出于技术上的原因, 在使用 `Google Test `_ 时我们允许测试固件类中的数据成员为 ``protected``. 336 | 337 | .. _declaration-order: 338 | 339 | 3.10. 声明顺序 340 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 341 | 342 | **总述** 343 | 344 | 将相似的声明放在一起, 将 ``public`` 部分放在最前. 345 | 346 | **说明** 347 | 348 | 类定义一般应以 ``public:`` 开始, 后跟 ``protected:``, 最后是 ``private:``. 省略空部分. 349 | 350 | 在各个部分中, 建议将类似的声明放在一起, 并且建议以如下的顺序: 类型 (包括 ``typedef``, ``using`` 和嵌套的结构体与类), 常量, 工厂函数, 构造函数, 赋值运算符, 析构函数, 其它函数, 数据成员. 351 | 352 | 不要将大段的函数定义内联在类定义中. 通常,只有那些普通的, 或性能关键且短小的函数可以内联在类定义中. 参见 :ref:`内联函数 ` 一节. 353 | 354 | 译者 (YuleFox) 笔记 355 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 356 | 357 | #. 不在构造函数中做太多逻辑相关的初始化; 358 | #. 编译器提供的默认构造函数不会对变量进行初始化, 如果定义了其他构造函数, 编译器不再提供, 需要编码者自行提供默认构造函数; 359 | #. 为避免隐式转换, 需将单参数构造函数声明为 ``explicit``; 360 | #. 为避免拷贝构造函数, 赋值操作的滥用和编译器自动生成, 可将其声明为 ``private`` 且无需实现; 361 | #. 仅在作为数据集合时使用 ``struct``; 362 | #. 组合 > 实现继承 > 接口继承 > 私有继承, 子类重载的虚函数也要声明 ``virtual`` 关键字, 虽然编译器允许不这样做; 363 | #. 避免使用多重继承, 使用时, 除一个基类含有实现外, 其他基类均为纯接口; 364 | #. 接口类类名以 ``Interface`` 为后缀, 除提供带实现的虚析构函数, 静态成员函数外, 其他均为纯虚函数, 不定义非静态数据成员, 不提供构造函数, 提供的话, 声明为 ``protected``; 365 | #. 为降低复杂性, 尽量不重载操作符, 模板, 标准类中使用时提供文档说明; 366 | #. 存取函数一般内联在头文件中; 367 | #. 声明次序: ``public`` -> ``protected`` -> ``private``; 368 | #. 函数体尽量短小, 紧凑, 功能单一; 369 | -------------------------------------------------------------------------------- /google-javascript-styleguide/javascript_language_rules.rst: -------------------------------------------------------------------------------- 1 | Javascript语言规范 2 | ===================== 3 | 4 | var关键字 5 | ---------------- 6 | 7 | 总是用 ``var`` 关键字定义变量。 8 | 9 | 描述 10 | ~~~~~~ 11 | 12 | 如果不显式使用 ``var`` 关键字定义变量,变量会进入到全局上下文中,可能会和已有的变量发生冲突。另外,如果不使用var声明,很难说变量存在的作用域是哪个(可能在局部作用域里,也可能在document或者window上)。所以,要一直使用 ``var`` 关键字定义变量。 13 | 14 | 常量 15 | ---------------- 16 | 17 | * 使用字母全部大写(如 ``NAMES_LIKE_THIS`` )的方式命名 18 | 19 | * 可以使用 ``@const`` 来标记一个常量 *指针* (指向变量或属性,自身不可变) 20 | 21 | * 由于IE的兼容问题,不要使用 `const关键字 `_ 22 | 23 | 描述 24 | ~~~~~~ 25 | 26 | 常量值 27 | ######## 28 | 29 | 如果一个值是恒定的,它命名中的字母要全部大写(如 ``CONSTANT_VALUE_CASE`` )。字母全部大写意味着这个值不可以被改写。 30 | 31 | 原始类型( ``number`` 、 ``string`` 、 ``boolean`` )是常量值。 32 | 33 | 对象的表现会更主观一些,当它们没有暴露出变化的时候,应该认为它们是常量。但是这个不是由编译器决定的。 34 | 35 | 常量指针(变量和属性) 36 | ######################## 37 | 38 | 用 ``@const`` 注释的变量和属性意味着它是不能更改的。使用const关键字可以保证在编译的时候保持一致。使用 `const `_ 效果相同,但是由于IE的兼容问题,我们不使用const关键字。 39 | 40 | 另外,不应该修改用 ``@const`` 注释的方法。 41 | 42 | 例子 43 | ######## 44 | 45 | 注意, ``@const`` 不一定是常量值,但命名类似 ``CONSTANT_VALUE_CASE`` 的 *一定* 是常量指针。 46 | 47 | :: 48 | 49 | /** 50 | * 以毫秒为单位的超时时长 51 | * @type {number} 52 | */ 53 | goog.example.TIMEOUT_IN_MILLISECONDS = 60; 54 | 55 | 1分钟60秒永远也不会改变,这是个常量。全部大写的命名意味其为常量值,所以它不能被重写。 56 | 开源的编译器允许这个符号被重写,这是因为 *没有* ``@const`` 标记。 57 | 58 | :: 59 | 60 | /** 61 | * Map of URL to response string. 62 | * @const 63 | */ 64 | MyClass.fetchedUrlCache_ = new goog.structs.Map(); 65 | 66 | 在这个例子中,指针没有变过,但是值却是可以变化的,所以这里用了驼峰式的命名,而不是全部大写的命名。 67 | 68 | 分号 69 | --------- 70 | 71 | 一定要使用分号。 72 | 73 | 依靠语句间隐式的分割,可能会造成细微的调试的问题,千万不要这样做。 74 | 75 | 很多时候不写分号是很危险的: 76 | 77 | :: 78 | 79 | // 1. 80 | MyClass.prototype.myMethod = function() { 81 | return 42; 82 | } // 这里没有分号. 83 | 84 | (function() { 85 | // 一些局部作用域中的初始化代码 86 | })(); 87 | 88 | var x = { 89 | 'i': 1, 90 | 'j': 2 91 | } //没有分号. 92 | 93 | // 2. 试着在IE和firefox下做一样的事情. 94 | //没人会这样写代码,别管他. 95 | [normalVersion, ffVersion][isIE](); 96 | 97 | var THINGS_TO_EAT = [apples, oysters, sprayOnCheese] //这里没有分号 98 | 99 | // 3. 条件语句 100 | -1 == resultOfOperation() || die(); 101 | 102 | 发生了什么? 103 | ~~~~~~~~~~~~~ 104 | 105 | 1. js错误。返回42的函数运行了,因为后面有一对括号,而且传入的参数是一个方法,然后返回的42被调用,导致出错了。 106 | 107 | 2. 你可能会得到一个“no sush property in undefined”的错误,因为在执行的时候,解释器将会尝试执行 ``x[normalVersion, ffVersion][isIE]()`` 这个方法。 108 | 109 | 3. ``die`` 这个方法只有在 ``resultOfOperation()`` 是 ``NaN`` 的时候执行,并且 ``THINGS_TO_EAT`` 将会被赋值为 ``die()`` 的结果。 110 | 111 | 为什么? 112 | ~~~~~~~~~~~~ 113 | 114 | js语句要求以分号结尾,除非能够正确地推断分号的位置。在这个例子当中,函数声明、对象和数组字面量被写在了一个语句当中。右括号(")"、"}"、"]")不足以证明这条语句已经结束了,如果下一个字符是运算符或者"("、"{"、"[",js将不会结束语句。 115 | 116 | 这个错误让人震惊,所以一定要确保用分号结束语句。 117 | 118 | 澄清:分号和函数 119 | ~~~~~~~~~~~~~~~~~~~ 120 | 121 | 函数表达式后面要分号结束,但是函数声明就不需要。例如: 122 | 123 | :: 124 | 125 | var foo = function() { 126 | return true; 127 | }; // 这里要分号 128 | 129 | function foo() { 130 | return true; 131 | } // 这里不用分号 132 | 133 | 嵌套函数 134 | ----------------- 135 | 136 | 可以使用。 137 | 138 | 嵌套函数非常有用,比如在创建持续任务或者隐藏工具方法的时候。可以放心的使用。 139 | 140 | 块内函数声明 141 | --------------------------- 142 | 143 | 不要使用块内函数声明。 144 | 145 | 不要这样做: 146 | 147 | :: 148 | 149 | if (x) { 150 | function foo() {} 151 | } 152 | 153 | 虽然大多数脚本引擎支持功能区块内声明,但ECMAScript并未认可(见 `ECMA-262 `_ ,第13条和第14)。若与他人的及EcmaScript所建议的不一致,即可视为不好的实现方式。ECMAScript只允许函数声明语句列表, 在根语句列表脚本或者函数。相反,使用一个变量初始化函数表达式在块内定义一个函数块: 154 | 155 | :: 156 | 157 | if (x) { 158 | var foo = function() {} 159 | } 160 | 161 | 异常 162 | ------- 163 | 164 | 可以抛出异常。 165 | 166 | 如果你做一些比较复杂的项目你基本上无法避免异常,比如使用一个应用程序开发框架。可以大胆试一试。 167 | 168 | 自定义异常 169 | ---------- 170 | 171 | 可以自定义异常。 172 | 173 | 如果没有自定义异常,返回的错误信息来自一个有返回值的函数是难处理的,是不雅的。坏的解决方案包括传递引用的类型来保存错误信息或总是返回有一个潜在的错误成员的对象。这些基本上为原始的异常处理hack。在适当的时候使用自定义的异常。 174 | 175 | 标准功能 176 | ---------- 177 | 178 | 总是优先于非标准功能。 179 | 180 | 为了最大的可移植性和兼容性,总是使用标准功能而不是非标准功能(例如,采用 `string.charAt(3)` 而非 `string[3]` ,用DOM的功能访问元素而不是使用特定于一个具体应用的简写)。 181 | 182 | 原始类型的包装对象 183 | ------------------ 184 | 185 | 没有理由使用原始类型的包装对象,更何况他们是危险的: 186 | 187 | :: 188 | 189 | var x = new Boolean(false); 190 | if (x) { 191 | alert('hi'); //显示“hi”。 192 | } 193 | 194 | 不要这样做! 195 | 196 | 然而类型转换是可以的。 197 | 198 | :: 199 | 200 | var x = Boolean(0); 201 | if (x) { 202 | alert('hi'); //永远都不显示。 203 | } 204 | typeof Boolean(0) == 'boolean'; 205 | typeof new Boolean(0) == 'object'; 206 | 207 | 这是非常有用的进行数字、字符串和布尔值转换的方式。 208 | 209 | 多重的原型继承 210 | ------------------- 211 | 212 | 不可取。 213 | 214 | 多重原型继承是Javascript实现继承的方式。如果你有一个以用户定义的class B作为原型的用户自定义class D,则得到多重原型继承。这样的继承出现容易但难以正确创造! 215 | 216 | 出于这个原因,最好是使用 `Closure库 `_ 中的 ``goog.inherits()`` 或类似的东西。 217 | 218 | :: 219 | 220 | function D() { 221 | goog.base(this) 222 | } 223 | goog.inherits( D, B ); 224 | 225 | D.prototype.method =function() { 226 | ... 227 | }; 228 | 229 | 方法和属性定义 230 | ------------------------- 231 | 232 | ``/**构造函数*/ function SomeConstructor() { this.someProperty = 1; } Foo.prototype.someMethod = function() { ... };`` 233 | 234 | 虽然有多种使用“new”关键词来创建对象方法和属性的途径,首选的创建方法的途径是: 235 | 236 | :: 237 | 238 | Foo.prototype.bar = function() { 239 | /* ... */ 240 | }; 241 | 242 | 其他特性的首选创建方式是在构造函数中初始化字段: 243 | 244 | :: 245 | 246 | /** @constructor */ 247 | function Foo() { 248 | this.bar = value; 249 | } 250 | 251 | 为什么? 252 | ~~~~~~~~~~ 253 | 254 | 当前的JavaScript引擎优化基于一个对象的“形状”, `给对象添加一个属性(包括覆盖原型设置的值)改变了形式,会降低性能 `_ 。 255 | 256 | 删除 257 | ---------- 258 | 259 | 请使用 ``this.foo = null`` 。 260 | 261 | :: 262 | 263 | o.prototype.dispose = function() { 264 | this.property_ = null; 265 | }; 266 | 267 | 而不是: 268 | 269 | :: 270 | 271 | Foo.prototype.dispose = function() { 272 | delete his.property_; 273 | }; 274 | 275 | 在现代的JavaScript引擎中,改变一个对象属性的数量比重新分配值慢得多。应该避免删除关键字,除非有必要从一个对象的迭代的关键字列表删除一个属性,或改变 ``if (key in obj)`` 结果。 276 | 277 | 闭包 278 | ------------- 279 | 280 | 可以使用,但是要小心。 281 | 282 | 创建闭包可能是JS最有用的和经常被忽视的功能。在 `这里 `_ 很好地描述说明了闭包的工作。 283 | 284 | 要记住的一件事情,一个闭包的指针指向包含它的范围。因此,附加一个闭包的DOM元素,可以创建一个循环引用,所以,内存会泄漏。例如,下面的代码: 285 | 286 | :: 287 | 288 | function foo(element, a, b) { 289 | element.onclick = function() { /* 使用 a 和 b */ }; 290 | } 291 | 292 | 闭包能保持元素a和b的引用即使它从未使用。因为元素还保持对闭包的一个引用,我们有一个循环引用,不会被垃圾收集清理。在这些情况下,代码的结构可以如下: 293 | 294 | :: 295 | 296 | function foo(element, a, b) { 297 | element.onclick = bar(a, b); 298 | } 299 | 300 | function bar(a, b) { 301 | return function() { /* 使用 a 和 b */ } 302 | } 303 | 304 | eval()函数 305 | ------------------------ 306 | 307 | 只用于反序列化(如评估RPC响应)。 308 | 309 | 若用于 ``eval()`` 的字符串含有用户输入,则 ``eval()`` 会造成混乱的语义,使用它有风险。通常有一个更好 310 | 更清晰、更安全的方式来编写你的代码,所以一般是不会允许其使用的。然而,eval相对比非eval使反序列化更容易,因此它的使用是可以接受的(例如评估RPC响应)。 311 | 312 | 反序列化是将一系列字节存到内存中的数据结构转化过程。例如,你可能会写的对象是: 313 | 314 | :: 315 | 316 | users = [ 317 | { 318 | name: 'Eric', 319 | id: 37824, 320 | email: 'jellyvore@myway.com' 321 | }, 322 | { 323 | name: 'xtof', 324 | id: 31337, 325 | email: 'b4d455h4x0r@google.com' 326 | }, 327 | ... 328 | ]; 329 | 330 | 将这些数据读入内存跟得出文件的字符串表示形式一样容易。 331 | 332 | 同样, ``eval()`` 函数可以简化解码RPC的返回值。例如,您可以使用 ``XMLHttpRequest`` 生成RPC,在响应时服务器返回JavaScript: 333 | 334 | :: 335 | 336 | var userOnline = false; 337 | var user = 'nusrat'; 338 | var xmlhttp = new XMLHttpRequest(); 339 | xmlhttp.open('GET', 'http://chat.google.com/isUserOnline?user=' + user, false); 340 | xmlhttp.send(''); 341 | // 服务器返回: 342 | // userOnline = true; 343 | if (xmlhttp.status == 200) { 344 | eval(xmlhttp.responseText); 345 | } 346 | // userOnline 现在为 true 347 | 348 | with() {} 349 | ---------------------- 350 | 351 | 不建议使用。 352 | 353 | 使用 ``with`` 会影响程序的语义。因为 ``with`` 的目标对象可能会含有和局部变量冲突的属性,使你程序的语义发生很大的变化。例如,这是做什么用? 354 | 355 | :: 356 | 357 | with (foo) { 358 | var x = 3; 359 | return x; 360 | } 361 | 362 | 答案:什么都有可能。局部变量 ``x`` 可能会被 ``foo`` 的一个属性覆盖,它甚至可能有setter方法,在此情况下将其赋值为3可能会执行很多其他代码。不要使用 ``with`` 。 363 | 364 | this 365 | ------------------- 366 | 367 | 只在构造函数对象、方法,和创建闭包的时候使用。 368 | 369 | ``this`` 的语义可能会非常诡异。有时它指向全局对象(很多时候)、调用者的作用域链(在 ``eval`` 里)、DOM树的一个节点(当使用HTML属性来做为事件句柄时)、新创建的对象(在一个构造函数中)、或者其他的对象(如果函数被 ``call()`` 或 ``apply()`` 方式调用)。 370 | 371 | 正因为 ``this`` 很容易被弄错,故将其使用限制在以下必须的地方: 372 | 373 | * 在构造函数中 374 | 375 | * 在对象的方法中(包括闭包的创建) 376 | 377 | for-in 循环 378 | ------------------ 379 | 380 | 只使用在对象、映射、哈希的键值迭代中。 381 | 382 | ``for-in`` 循环经常被不正确的用在元素数组的循环中。由于并不是从 ``0`` 到 ``length-1`` 进行循环,而是遍历对象中和它原型链上的所有的键,所以很容易出错。这里有一些失败的例子: 383 | 384 | :: 385 | 386 | function printArray(arr) { 387 | for (var key in arr) { 388 | print(arr[key]); 389 | } 390 | } 391 | 392 | printArray([0,1,2,3]); //这样可以 393 | 394 | var a = new Array(10); 395 | printArray(a); //这样不行 396 | 397 | a = document.getElementsByTagName('*'); 398 | printArray(a); //这样不行 399 | 400 | a = [0,1,2,3]; 401 | a.buhu = 'wine'; 402 | printArray(a); //这样不行 403 | 404 | a = new Array; 405 | a[3] = 3; 406 | printArray(a); //这样不行 407 | 408 | 在数组循环时常用的一般方式: 409 | 410 | :: 411 | 412 | function printArray(arr) { 413 | var l = arr.length; 414 | for (var i = 0; i < l; i++) { 415 | print(arr[i]); 416 | } 417 | } 418 | 419 | 关联数组 420 | ----------------------- 421 | 422 | 不要将映射,哈希,关联数组当作一般数组来使用。 423 | 424 | 不允许使用关联数组……确切的说在数组,你不可以使用非数字的索引。如果你需要一个映射或者哈希,在这种情况下你应该使用对象来代替数组,因为在功能上你真正需要的是对象的特性而不是数组的。 425 | 426 | 数组仅仅是用来拓展对象的(像在JS中你曾经使用过的 ``Date`` 、 ``RegExp`` 和 ``String`` 对象一样的)。 427 | 428 | 多行的字符串字面量 429 | ------------------------------------ 430 | 431 | 不要使用。 432 | 433 | 不要这样做: 434 | 435 | :: 436 | 437 | var myString = 'A rather long string of English text, an error message \ 438 | actually that just keeps going and going -- an error \ 439 | message to make the Energizer bunny blush (right through \ 440 | those Schwarzenegger shades)! Where was I? Oh yes, \ 441 | you\'ve got an error and all the extraneous whitespace is \ 442 | just gravy. Have a nice day.'; 443 | 444 | 在编译时每一行头部的空白符不会被安全地去除掉;斜线后的空格也会导致棘手的问题;虽然大部分脚本引擎都会支持,但是它不是ECMAScript规范的一部分。 445 | 446 | 使用字符串连接来代替: 447 | 448 | :: 449 | 450 | var myString = 'A rather long string of English text, an error message ' + 451 | 'actually that just keeps going and going -- an error ' + 452 | 'message to make the Energizer bunny blush (right through ' + 453 | 'those Schwarzenegger shades)! Where was I? Oh yes, ' + 454 | 'you\'ve got an error and all the extraneous whitespace is ' + 455 | 'just gravy. Have a nice day.'; 456 | 457 | 数组和对象字面量 458 | ---------------------------------- 459 | 460 | 建议使用。 461 | 462 | 使用数组和对象字面量来代替数组和对象构造函数。 463 | 464 | 数组构造函数容易在参数上出错。 465 | 466 | :: 467 | 468 | // 长度为3 469 | var a1 = new Array(x1, x2, x3); 470 | 471 | // 长度为 2 472 | var a2 = new Array(x1, x2); 473 | 474 | // If x1 is a number and it is a natural number the length will be x1. 475 | // If x1 is a number but not a natural number this will throw an exception. 476 | // Otherwise the array will have one element with x1 as its value. 477 | var a3 = new Array(x1); 478 | 479 | // 长度为0 480 | var a4 = new Array(); 481 | 482 | 由此,如果有人将代码从2个参数变成了一个参数,那么这个数组就会有一个错误的长度。 483 | 484 | 为了避免这种怪异的情况,永远使用可读性更好的数组字面量。 485 | 486 | :: 487 | 488 | var a = [x1, x2, x3]; 489 | var a2 = [x1, x2]; 490 | var a3 = [x1]; 491 | var a4 = []; 492 | 493 | 对象构造函数虽然没有相同的问题,但是对于可读性和一致性,还是应该使用对象字面量。 494 | 495 | :: 496 | 497 | var o = new Object(); 498 | 499 | var o2 = new Object(); 500 | o2.a = 0; 501 | o2.b = 1; 502 | o2.c = 2; 503 | o2['strange key'] = 3; 504 | 505 | 应该写成: 506 | 507 | :: 508 | 509 | var o = {}; 510 | 511 | var o2 = { 512 | a: 0, 513 | b: 1, 514 | c: 2, 515 | 'strange key': 3 516 | }; 517 | 518 | 修改内置对象原型 519 | -------------------------------- 520 | 521 | 不建议。 522 | 523 | 强烈禁止修改如 ``Object.prototype`` 和 ``Array.prototype`` 等对象的原型。修改其他内置原型如 ``Function.prototype`` 危险性较小,但在生产环境中还是会引发一些难以调试的问题,也应当避免。 524 | 525 | Internet Explorer中的条件注释 526 | ---------------------------------------------------------- 527 | 528 | 不要使用。 529 | 530 | 不要这样做: 531 | 532 | :: 533 | 534 | var f = function () { 535 | /*@cc_on if (@_jscript) { return 2* @*/ 3; /*@ } @*/ 536 | }; 537 | 538 | 条件注释会在运行时改变JavaScript语法树,阻碍自动化工具。 539 | -------------------------------------------------------------------------------- /google-objc-styleguide/features.rst: -------------------------------------------------------------------------------- 1 | Cocoa 和 Objective-C 特性 2 | ============================== 3 | 4 | 5 | 成员变量应该是 ``@private`` 6 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 7 | 8 | .. tip:: 9 | 10 | 成员变量应该声明为 ``@private`` 11 | 12 | .. code-block:: objc 13 | 14 | @interface MyClass : NSObject { 15 | @private 16 | id myInstanceVariable_; 17 | } 18 | // public accessors, setter takes ownership 19 | - (id)myInstanceVariable; 20 | - (void)setMyInstanceVariable:(id)theVar; 21 | @end 22 | 23 | 24 | 25 | 明确指定构造函数 26 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 27 | 28 | .. tip:: 29 | 30 | 注释并且明确指定你的类的构造函数。 31 | 32 | 对于需要继承你的类的人来说,明确指定构造函数十分重要。这样他们就可以只重写一个构造函数(可能是几个)来保证他们的子类的构造函数会被调用。这也有助于将来别人调试你的类时,理解初始化代码的工作流程。 33 | 34 | 35 | 重载指定构造函数 36 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 37 | 38 | .. tip:: 39 | 40 | 当你写子类的时候,如果需要 ``init…`` 方法,记得重载父类的指定构造函数。 41 | 42 | 如果你没有重载父类的指定构造函数,你的构造函数有时可能不会被调用,这会导致非常隐秘而且难以解决的 bug。 43 | 44 | 45 | 重载 ``NSObject`` 的方法 46 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 47 | 48 | .. tip:: 49 | 50 | 如果重载了 ``NSObject`` 类的方法,强烈建议把它们放在 ``@implementation`` 内的起始处,这也是常见的操作方法。 51 | 52 | 通常适用(但不局限)于 ``init...``,``copyWithZone:``,以及 ``dealloc`` 方法。所有 ``init...`` 方法应该放在一起,``copyWithZone:`` 紧随其后,最后才是 ``dealloc`` 方法。 53 | 54 | 初始化 55 | ~~~~~~~~~~~ 56 | 57 | .. tip:: 58 | 59 | 不要在 init 方法中,将成员变量初始化为 ``0`` 或者 ``nil``;毫无必要。 60 | 61 | 刚分配的对象,默认值都是 0,除了 ``isa`` 指针(译者注:``NSObject`` 的 ``isa`` 指针,用于标识对象的类型)。所以不要在初始化器里面写一堆将成员初始化为 ``0`` 或者 ``nil`` 的代码。 62 | 63 | 64 | 避免 ``+new`` 65 | ~~~~~~~~~~~~~~~ 66 | 67 | .. tip:: 68 | 69 | 不要调用 ``NSObject`` 类方法 ``new``,也不要在子类中重载它。使用 ``alloc`` 和 ``init`` 方法创建并初始化对象。 70 | 71 | 72 | 现代的 Objective-C 代码通过调用 ``alloc`` 和 ``init`` 方法来创建并 retain 一个对象。由于类方法 ``new`` 很少使用,这使得有关内存分配的代码审查更困难。 73 | 74 | 保持公共 API 简单 75 | ~~~~~~~~~~~~~~~~~~~~~~~ 76 | 77 | .. tip:: 78 | 79 | 保持类简单;避免 “厨房水槽(kitchen-sink)” 式的 API。如果一个函数压根没必要公开,就不要这么做。用私有类别保证公共头文件整洁。 80 | 81 | 与 C++ 不同,Objective-C 没有方法来区分公共的方法和私有的方法 -- 所有的方法都是公共的(译者注:这取决于 Objective-C 运行时的方法调用的消息机制)。因此,除非客户端的代码期望使用某个方法,不要把这个方法放进公共 API 中。尽可能的避免了你你不希望被调用的方法却被调用到。这包括重载父类的方法。对于内部实现所需要的方法,在实现的文件中定义一个类别,而不是把它们放进公有的头文件中。 82 | 83 | .. code-block:: objc 84 | 85 | // GTMFoo.m 86 | #import "GTMFoo.h" 87 | 88 | @interface GTMFoo (PrivateDelegateHandling) 89 | - (NSString *)doSomethingWithDelegate; // Declare private method 90 | @end 91 | 92 | @implementation GTMFoo(PrivateDelegateHandling) 93 | ... 94 | - (NSString *)doSomethingWithDelegate { 95 | // Implement this method 96 | } 97 | ... 98 | @end 99 | 100 | Objective-C 2.0 以前,如果你在私有的 ``@interface`` 中声明了某个方法,但在 ``@implementation`` 中忘记定义这个方法,编译器不会抱怨(这是因为你没有在其它的类别中实现这个私有的方法)。解决文案是将方法放进指定类别的 ``@implemenation`` 中。 101 | 102 | 如果你在使用 Objective-C 2.0,相反你应该使用 `类扩展 `_ 来声明你的私有类别,例如: 103 | 104 | .. code-block:: objc 105 | 106 | @interface GMFoo () { ... } 107 | 108 | 这么做确保如果声明的方法没有在 ``@implementation`` 中实现,会触发一个编译器告警。 109 | 110 | 再次说明,“私有的” 方法其实不是私有的。你有时可能不小心重载了父类的私有方法,因而制造出很难查找的 Bug。通常,私有的方法应该有一个相当特殊的名字以防止子类无意地重载它们。 111 | 112 | Objective-C 的类别可以用来将一个大的 ``@implementation`` 拆分成更容易理解的小块,同时,类别可以为最适合的类添加新的、特定应用程序的功能。例如,当添加一个 “middle truncation” 方法时,创建一个 ``NSString`` 的新类别并把方法放在里面,要比创建任意的一个新类把方法放进里面好得多。 113 | 114 | ``#import`` and ``#include`` 115 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 116 | 117 | .. tip:: 118 | 119 | ``#import`` Objective-C/Objective-C++ 头文件,``#include`` C/C++ 头文件。 120 | 121 | 基于你所包括的头文件的编程语言,选择使用 ``#import`` 或是 ``#include``: 122 | 123 | * 当包含一个使用 Objective-C、Objective-C++ 的头文件时,使用 ``#import`` 。 124 | * 当包含一个使用标准 C、C++ 头文件时,使用 ``#include``。头文件应该使用 `#define 保护 `_。 125 | 126 | 一些 Objective-C 的头文件缺少 ``#define`` 保护,需要使用 ``#import`` 的方式包含。由于 Objective-C 的头文件只会被 Objective-C 的源文件及头文件包含,广泛地使用 ``#import`` 是可以的。 127 | 128 | 文件中没有 Objective-C 代码的标准 C、C++ 头文件,很可能会被普通的 C、C++ 包含。由于标准 C、C++ 里面没有 ``#import`` 的用法,这些文件将被 ``#include``。在 Objective-C 源文件中使用 ``#include`` 包含这些头文件,意味着这些头文件永远会在相同的语义下包含。 129 | 130 | 这条规则帮助跨平台的项目避免低级错误。某个 Mac 开发者写了一个新的 C 或 C++ 头文件,如果忘记使用 ``#define`` 保护,在 Mac 下使用 ``#import`` 这个头文件不回引起问题,但是在其它平台下使用 ``#include`` 将可能编译失败。在所有的平台上统一使用 ``#include``,意味着构造更可能全都成功或者失败,防止这些文件只能在某些平台下能够工作。 131 | 132 | .. code-block:: objc 133 | 134 | #import 135 | #include 136 | #import "GTMFoo.h" 137 | #include "base/basictypes.h" 138 | 139 | 140 | 使用根框架 141 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 142 | 143 | .. tip:: 144 | 145 | ``#import`` 根框架而不是单独的零散文件 146 | 147 | 当你试图从框架(如 Cocoa 或者 Foundation)中包含若干零散的系统头文件时,实际上包含顶层根框架的话,编译器要做的工作更少。根框架通常已经经过预编译,加载更快。另外记得使用 ``#import`` 而不是 ``#include`` 来包含 Objective-C 的框架。 148 | 149 | .. code-block:: objc 150 | 151 | #import // good 152 | 153 | #import // avoid 154 | #import 155 | ... 156 | 157 | 158 | 构建时即设定 ``autorelease`` 159 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 160 | 161 | .. tip:: 162 | 163 | 当创建临时对象时,在同一行使用 ``autolease``,而不是在同一个方法的后面语句中使用一个单独的 ``release``。 164 | 165 | 尽管运行效率会差一点,但避免了意外删除 ``release`` 或者插入 ``return`` 语句而导致内存泄露的可能。例如: 166 | 167 | .. code-block:: objc 168 | 169 | // AVOID (unless you have a compelling performance reason) 170 | MyController* controller = [[MyController alloc] init]; 171 | // ... code here that might return ... 172 | [controller release]; 173 | 174 | // BETTER 175 | MyController* controller = [[[MyController alloc] init] autorelease]; 176 | 177 | 178 | ``autorelease`` 优先 ``retain`` 其次 179 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 180 | 181 | .. tip:: 182 | 183 | 给对象赋值时遵守 ``autorelease``之后 ``retain`` 的模式。 184 | 185 | 当给一个变量赋值新的对象时,必须先释放掉旧的对象以避免内存泄露。有很多 “正确的” 方法可以处理这种情况。我们则选择 “``autorelease`` 之后 ``retain``” 的方法,因为事实证明它不容易出错。注意大的循环会填满 ``autorelease`` 池,并且可能效率上会差一点,但权衡之下我们认为是可以接受的。 186 | 187 | .. code-block:: objc 188 | 189 | - (void)setFoo:(GMFoo *)aFoo { 190 | [foo_ autorelease]; // Won't dealloc if |foo_| == |aFoo| 191 | foo_ = [aFoo retain]; 192 | } 193 | 194 | 195 | ``init`` 和 ``dealloc`` 内避免使用访问器 196 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 197 | 198 | .. tip:: 199 | 200 | 在 ``init`` 和 ``dealloc`` 方法执行的过程中,子类可能会处在一个不一致的状态,所以这些方法中的代码应避免调用访问器。 201 | 202 | 子类尚未初始化,或在 ``init`` 和 ``dealloc`` 方法执行时已经被销毁,会使访问器方法很可能不可靠。实际上,应在这些方法中直接对 ivals 进行赋值或释放操作。 203 | 204 | 正确: 205 | 206 | .. code-block:: objc 207 | 208 | - (id)init { 209 | self = [super init]; 210 | if (self) { 211 | bar_ = [[NSMutableString alloc] init]; // good 212 | } 213 | return self; 214 | } 215 | 216 | - (void)dealloc { 217 | [bar_ release]; // good 218 | [super dealloc]; 219 | } 220 | 221 | 错误: 222 | 223 | .. code-block:: objc 224 | 225 | - (id)init { 226 | self = [super init]; 227 | if (self) { 228 | self.bar = [NSMutableString string]; // avoid 229 | } 230 | return self; 231 | } 232 | 233 | - (void)dealloc { 234 | self.bar = nil; // avoid 235 | [super dealloc]; 236 | } 237 | 238 | 239 | 按声明顺序销毁实例变量 240 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 241 | 242 | .. tip:: 243 | 244 | ``dealloc`` 中实例变量被释放的顺序应该与它们在 ``@interface`` 中声明的顺序一致,这有助于代码审查。 245 | 246 | 代码审查者在评审新的或者修改过的 ``dealloc`` 实现时,需要保证每个 ``retained`` 的实例变量都得到了释放。 247 | 248 | 为了简化 ``dealloc`` 的审查,``retained`` 实例变量被释放的顺序应该与他们在 ``@interface`` 中声明的顺序一致。如果 ``dealloc`` 调用了其它方法释放成员变量,添加注释解释这些方法释放了哪些实例变量。 249 | 250 | ``setter`` 应复制 NSStrings 251 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 252 | 253 | .. tip:: 254 | 255 | 接受 ``NSString`` 作为参数的 ``setter``,应该总是 ``copy`` 传入的字符串。 256 | 257 | 永远不要仅仅 ``retain`` 一个字符串。因为调用者很可能在你不知情的情况下修改了字符串。不要假定别人不会修改,你接受的对象是一个 ``NSString`` 对象而不是 ``NSMutableString`` 对象。 258 | 259 | .. code-block:: objc 260 | 261 | - (void)setFoo:(NSString *)aFoo { 262 | [foo_ autorelease]; 263 | foo_ = [aFoo copy]; 264 | } 265 | 266 | 267 | 268 | .. _avoid-throwing-exceptions: 269 | 270 | 避免抛异常 271 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 272 | 273 | .. tip:: 274 | 275 | 不要 ``@throw`` Objective-C 异常,同时也要时刻准备捕获从第三方或 OS 代码中抛出的异常。 276 | 277 | 我们的确允许 ``-fobjc-exceptions`` 编译开关(主要因为我们要用到 ``@synchronized`` ),但我们不使用 ``@throw``。为了合理使用第三方的代码,``@try``、``@catch`` 和 ``@finally`` 是允许的。如果你确实使用了异常,请明确注释你期望什么方法抛出异常。 278 | 279 | 不要使用 ``NS_DURING``、``NS_HANDLER``、``NS_ENDHANDLER``、``NS_VALUERETURN`` 和 ``NS_VOIDRETURN`` 宏,除非你写的代码需要在 Mac OS X 10.2 或之前的操作系统中运行。 280 | 281 | 注意:如果抛出 Objective-C 异常,Objective-C++ 代码中基于栈的对象不会被销毁。比如: 282 | 283 | .. code-block:: objc 284 | 285 | class exceptiontest { 286 | public: 287 | exceptiontest() { NSLog(@"Created"); } 288 | ~exceptiontest() { NSLog(@"Destroyed"); } 289 | }; 290 | 291 | void foo() { 292 | exceptiontest a; 293 | NSException *exception = [NSException exceptionWithName:@"foo" 294 | reason:@"bar" 295 | userInfo:nil]; 296 | @throw exception; 297 | } 298 | 299 | int main(int argc, char *argv[]) { 300 | GMAutoreleasePool pool; 301 | @try { 302 | foo(); 303 | } 304 | @catch(NSException *ex) { 305 | NSLog(@"exception raised"); 306 | } 307 | return 0; 308 | } 309 | 310 | 会输出: 311 | 312 | .. :: 313 | 314 | 2006-09-28 12:34:29.244 exceptiontest[23661] Created 315 | 2006-09-28 12:34:29.244 exceptiontest[23661] exception raised 316 | 317 | 318 | 注意:这里析构函数从未被调用。这主要会影响基于栈的 ``smartptr``,比如 ``shared_ptr``、``linked_ptr``,以及所有你可能用到的 STL 对象。因此我们不得不痛苦的说,如果必须在 Objective-C++ 中使用异常,就只用 C++ 的异常机制。永远不应该重新抛出 Objective-C 异常,也不应该在 ``@try``、``@catch`` 或 ``@finally`` 语句块中使用基于栈的 C++ 对象。 319 | 320 | nil 检查 321 | ~~~~~~~~~~~~~~ 322 | 323 | .. tip:: 324 | 325 | ``nil`` 检查只用在逻辑流程中。 326 | 327 | 328 | 329 | 使用 ``nil`` 的检查来检查应用程序的逻辑流程,而不是避免崩溃。Objective-C 运行时会处理向 ``nil`` 对象发送消息的情况。如果方法没有返回值,就没关系。如果有返回值,可能由于运行时架构、返回值类型以及 OS X 版本的不同而不同,参见 `Apple’s documentation `_ 。 330 | 331 | 注意,这和 C/C++ 中检查指针是否为 ‵‵NULL`` 很不一样,C/C++ 运行时不做任何检查,从而导致应用程序崩溃。因此你仍然需要保证你不会对一个 C/C++ 的空指针解引用。 332 | 333 | BOOL 若干陷阱 334 | ~~~~~~~~~~~~~~~~~~~~~ 335 | 336 | .. tip:: 337 | 338 | 将普通整形转换成 ``BOOL`` 时要小心。不要直接将 ``BOOL`` 值与 ``YES`` 进行比较。 339 | 340 | Objective-C 中把 ``BOOL`` 定义成无符号字符型,这意味着 ``BOOL`` 类型的值远不止 ``YES``(1)或 ``NO``(0)。不要直接把整形转换成 ``BOOL``。常见的错误包括将数组的大小、指针值及位运算的结果直接转换成 ``BOOL`` ,取决于整型结果的最后一个字节,很可能会产生一个 ``NO`` 值。当转换整形至 ``BOOL`` 时,使用三目操作符来返回 ``YES`` 或者 ``NO``。(译者注:读者可以试一下任意的 256 的整数的转换结果,如 256、512 …) 341 | 342 | 你可以安全在 ``BOOL``、``_Bool`` 以及 ``bool`` 之间转换(参见 C++ Std 4.7.4, 4.12 以及 C99 Std 6.3.1.2)。你不能安全在 ``BOOL`` 以及 ``Boolean`` 之间转换,因此请把 ``Boolean`` 当作一个普通整形,就像之前讨论的那样。但 Objective-C 的方法标识符中,只使用 ``BOOL``。 343 | 344 | 对 ``BOOL`` 使用逻辑运算符(``&&``,``||`` 和 ``!``)是合法的,返回值也可以安全地转换成 ``BOOL``,不需要使用三目操作符。 345 | 346 | 错误的用法: 347 | 348 | .. code-block:: objc 349 | 350 | - (BOOL)isBold { 351 | return [self fontTraits] & NSFontBoldTrait; 352 | } 353 | - (BOOL)isValid { 354 | return [self stringValue]; 355 | } 356 | 357 | 358 | 正确的用法: 359 | 360 | .. code-block:: objc 361 | 362 | - (BOOL)isBold { 363 | return ([self fontTraits] & NSFontBoldTrait) ? YES : NO; 364 | } 365 | - (BOOL)isValid { 366 | return [self stringValue] != nil; 367 | } 368 | - (BOOL)isEnabled { 369 | return [self isValid] && [self isBold]; 370 | } 371 | 372 | 373 | 同样,不要直接比较 ``YES/NO`` 和 ``BOOL`` 变量。不仅仅因为影响可读性,更重要的是结果可能与你想的不同。 374 | 375 | 错误的用法: 376 | 377 | .. code-block:: objc 378 | 379 | BOOL great = [foo isGreat]; 380 | if (great == YES) 381 | // ...be great! 382 | 383 | 正确的用法: 384 | 385 | .. code-block:: objc 386 | 387 | BOOL great = [foo isGreat]; 388 | if (great) 389 | // ...be great! 390 | 391 | 属性(Property) 392 | ~~~~~~~~~~~~~~~~~~~~~ 393 | 394 | .. tip:: 395 | 396 | 属性(Property)通常允许使用,但需要清楚的了解:属性(Property)是 Objective-C 2.0 的特性,会限制你的代码只能跑在 iPhone 和 Mac OS X 10.5 (Leopard) 及更高版本上。点引用只允许访问声明过的 ``@property``。 397 | 398 | 命名 399 | ^^^^^^ 400 | 401 | 属性所关联的实例变量的命名必须遵守以下划线作为后缀的规则。属性的名字应该与成员变量去掉下划线后缀的名字一模一样。 402 | 403 | 使用 ``@synthesize`` 指示符来正确地重命名属性。 404 | 405 | .. code-block:: objc 406 | 407 | @interface MyClass : NSObject { 408 | @private 409 | NSString *name_; 410 | } 411 | @property(copy, nonatomic) NSString *name; 412 | @end 413 | 414 | @implementation MyClass 415 | @synthesize name = name_; 416 | @end 417 | 418 | 419 | 位置 420 | ^^^^^^ 421 | 422 | 属性的声明必须紧靠着类接口中的实例变量语句块。属性的定义必须在 ``@implementation`` 的类定义的最上方。他们的缩进与包含他们的 ``@interface`` 以及 ``@implementation`` 语句一样。 423 | 424 | .. code-block:: objc 425 | 426 | @interface MyClass : NSObject { 427 | @private 428 | NSString *name_; 429 | } 430 | @property(copy, nonatomic) NSString *name; 431 | @end 432 | 433 | @implementation MyClass 434 | @synthesize name = name_; 435 | - (id)init { 436 | ... 437 | } 438 | @end 439 | 440 | 441 | 字符串应使用 ``copy`` 属性(Attribute) 442 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 443 | 444 | 应总是用 ``copy`` 属性(attribute)声明 ``NSString`` 属性(property)。 445 | 446 | 从逻辑上,确保遵守 ``NSString`` 的 ``setter`` 必须使用 ``copy`` 而不是 ``retain`` 的原则。 447 | 448 | 449 | 原子性 450 | ^^^^^^^^^^ 451 | 452 | 一定要注意属性(property)的开销。缺省情况下,所有 ``synthesize`` 的 ``setter`` 和 ``getter`` 都是原子的。这会给每个 ``get`` 或者 ``set`` 带来一定的同步开销。将属性(property)声明为 ``nonatomic``,除非你需要原子性。 453 | 454 | 455 | 点引用 456 | ^^^^^^^^^^ 457 | 458 | 点引用是地道的 Objective-C 2.0 风格。它被使用于简单的属性 ``set``、``get`` 操作,但不应该用它来调用对象的其它操作。 459 | 460 | 正确的做法: 461 | 462 | .. code-block:: objc 463 | 464 | NSString *oldName = myObject.name; 465 | myObject.name = @"Alice"; 466 | 467 | 错误的做法: 468 | 469 | .. code-block:: objc 470 | 471 | NSArray *array = [[NSArray arrayWithObject:@"hello"] retain]; 472 | 473 | NSUInteger numberOfItems = array.count; // not a property 474 | array.release; // not a property 475 | 476 | 477 | 没有实例变量的接口 478 | ~~~~~~~~~~~~~~~~~~~~~ 479 | 480 | .. tip:: 481 | 482 | 没有声明任何实例变量的接口,应省略空花括号。 483 | 484 | 正确的做法: 485 | 486 | @interface MyClass : NSObject 487 | // Does a lot of stuff 488 | - (void)fooBarBam; 489 | @end 490 | 491 | 错误的做法: 492 | 493 | @interface MyClass : NSObject { 494 | } 495 | // Does a lot of stuff 496 | - (void)fooBarBam; 497 | @end 498 | 499 | 500 | 501 | 自动 ``synthesize`` 实例变量 502 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 503 | 504 | .. tip:: 505 | 506 | 只运行在 iOS 下的代码,优先考虑使用自动 ``synthesize`` 实例变量。 507 | 508 | ``synthesize`` 实例变量时,使用 ``@synthesize var = var_;`` 防止原本想调用 ``self.var = blah;`` 却不慎写成了 ``var = blah;``。 509 | 510 | 511 | 不要synthesize CFType的属性 CFType应该永远使用@dynamic实现指示符。 尽管CFType不能使用retain属性特性,开发者必须自己处理retain和release。很少有情况你需要仅仅对它进行赋值,因此最好显示地实现getter和setter,并作出注释说明。 列出所有的实现指示符 尽管@dynamic是默认的,显示列出它以及其它的实现指示符会提高可读性,代码阅读者可以一眼就知道类的每个属性是如何实现的。 512 | 513 | .. code-block:: objc 514 | 515 | // Header file 516 | @interface Foo : NSObject 517 | // A guy walks into a bar. 518 | @property(nonatomic, copy) NSString *bar; 519 | @end 520 | 521 | // Implementation file 522 | @interface Foo () 523 | @property(nonatomic, retain) NSArray *baz; 524 | @end 525 | 526 | @implementation Foo 527 | @synthesize bar = bar_; 528 | @synthesize baz = baz_; 529 | @end 530 | 531 | -------------------------------------------------------------------------------- /google-typescript-styleguide/type_system.rst: -------------------------------------------------------------------------------- 1 | 类型系统 2 | ################################################################################ 3 | 4 | .. _ts-type-inference: 5 | 6 | 类型推导 7 | ******************************************************************************** 8 | 9 | 对于所有类型的表达式(包括变量、字段、返回值,等等),都可以依赖 TypeScript 编译器所实现的类型推导。 google3 编译器会拒绝所有缺少类型记号又无法推导出其类型的代码,以保证所有的代码都具有类型(即使其中可能包括显式的 ``any`` 类型)。 10 | 11 | .. code-block:: typescript 12 | 13 | const x = 15; // x 的类型可以推导得出. 14 | 15 | 当变量或参数被初始化为 ``string`` , ``number`` , ``boolean`` , ``RegExp`` 正则表达式字面量或 ``new`` 表达式时,由于明显能够推导出类型,因此应当省略类型记号。 16 | 17 | .. code-block:: typescript 18 | 19 | // 不要这样做!添加 boolean 记号对提高可读性没有任何帮助! 20 | const x: boolean = true; 21 | 22 | .. code-block:: typescript 23 | 24 | // 不要这样做!Set 类型显然可以从初始化语句中推导得出。 25 | const x: Set = new Set(); 26 | 27 | .. code-block:: typescript 28 | 29 | // 应当这样做!依赖 TypeScript 的类型推导。 30 | const x = new Set(); 31 | 32 | 33 | 对于更为复杂的表达式,类型记号有助于提高代码的可读性。此时是否使用类型记号应当由代码审查员决定。 34 | 35 | .. _ts-return-types: 36 | 37 | 返回类型 38 | ================================================================================ 39 | 40 | 代码的作者可以自由决定是否在函数和方法中使用类型记号标明返回类型。代码审查员 *可以* 要求对难以理解的复杂返回类型使用类型记号进行阐明。项目内部 *可以* 自行规定必须标明返回值,本文作为一个通用的 TypeScript 风格指南,不做硬性要求。 41 | 42 | 显式地标明函数和方法的返回值有两个优点: 43 | 44 | * 能够生成更精确的文档,有助于读者理解代码。 45 | * 如果未来改变了函数的返回类型的话,可以让因此导致的潜在的错误更快地暴露出来。 46 | 47 | .. _ts-null-vs-undefined: 48 | 49 | Null 还是 Undefined ? 50 | ******************************************************************************** 51 | 52 | TypeScript 支持 ``null`` 和 ``undefined`` 类型。可空类型可以通过联合类型实现,例如 ``string | null`` 。对于 ``undefined`` 也是类似的。对于 ``null`` 和 ``undefined`` 的联合类型,并无特殊的语法。 53 | 54 | TypeScript 代码中可以使用 ``undefined`` 或者 ``null`` 标记缺少的值,这里并无通用的规则约定应当使用其中的某一种。许多 JavaScript API 使用 ``undefined`` (例如 ``Map.get`` ),然而 DOM 和 Google API 中则更多地使用 ``null`` (例如 ``Element.getAttribute`` ),因此,对于 ``null`` 和 ``undefined`` 的选择取决于当前的上下文。 55 | 56 | .. _ts-nullable-undefined-type-aliases: 57 | 58 | 可空/未定义类型别名 59 | ================================================================================ 60 | 61 | *不允许* 为包括 ``|null`` 或 ``|undefined`` 的联合类型创建类型别名。这种可空的别名通常意味着空值在应用中会被层层传递,并且它掩盖了导致空值出现的源头。另外,这种别名也让类或接口中的某个值何时有可能为空变得不确定。 62 | 63 | 因此,代码 *必须* 在使用别名时才允许添加 ``|null`` 或者 ``|undefined`` 。同时,代码 *应当* 在空值出现位置的附近对其进行处理。 64 | 65 | .. code-block:: typescript 66 | 67 | // 不要这样做!不要在创建别名的时候包含 undefined ! 68 | type CoffeeResponse = Latte|Americano|undefined; 69 | 70 | class CoffeeService { 71 | getLatte(): CoffeeResponse { ... }; 72 | } 73 | 74 | .. code-block:: typescript 75 | 76 | // 应当这样做!在使用别名的时候联合 undefined ! 77 | type CoffeeResponse = Latte|Americano; 78 | 79 | class CoffeeService { 80 | getLatte(): CoffeeResponse|undefined { ... }; 81 | } 82 | 83 | 84 | .. code-block:: typescript 85 | 86 | // 这样做更好!使用断言对可能的空值进行处理! 87 | type CoffeeResponse = Latte|Americano; 88 | 89 | class CoffeeService { 90 | getLatte(): CoffeeResponse { 91 | return assert(fetchResponse(), 'Coffee maker is broken, file a ticket'); 92 | }; 93 | } 94 | 95 | .. _ts-optionals-vs-undefined-type: 96 | 97 | 可选参数 还是 ``undefined`` 类型? 98 | ================================================================================ 99 | 100 | TypeScript 支持使用 ``?`` 创建可选参数和可选字段,例如: 101 | 102 | .. code-block:: typescript 103 | 104 | interface CoffeeOrder { 105 | sugarCubes: number; 106 | milk?: Whole|LowFat|HalfHalf; 107 | } 108 | 109 | function pourCoffee(volume?: Milliliter) { ... } 110 | 111 | 112 | 可选参数实际上隐式地向类型中联合了 ``|undefined`` 。不同之处在于,在构造类实例或调用方法时,可选参数可以被直接省略。例如, ``{sugarCubes: 1}`` 是一个合法的 ``CoffeeOrder`` ,因为 ``milk`` 字段是可选的。 113 | 114 | 应当使用可选字段(对于类或者接口)和可选参数而非联合 ``|undefined`` 类型。 115 | 116 | 对于类,应当尽可能避免使用可选字段,尽可能初始化每一个字段。 117 | 118 | .. code-block:: typescript 119 | 120 | class MyClass { 121 | field = ''; 122 | } 123 | 124 | .. _ts-structural-types-vs-nominal-types: 125 | 126 | 结构类型 与 指名类型 127 | ******************************************************************************** 128 | 129 | TypeScript 的类型系统使用的是结构类型而非指名类型。具体地说,一个值,如果它拥有某个类型的所有属性,且所有属性的类型能够递归地一一匹配,则这个值与这个类型也是匹配的。 130 | 131 | 在代码中,可以在适当的场景使用结构类型。具体地说,在测试代码之外,应当使用接口而非类对结构类型进行定义。在测试代码中,由于经常要创建 Mock 对象用于测试,此时不引入额外的接口往往较为方便。 132 | 133 | 在提供基于结构类型的实现时,应当在符号的声明位置显式地包含其类型,使类型检查和错误检测能够更准确地工作。 134 | 135 | .. code-block:: typescript 136 | 137 | // 应当这样做! 138 | const foo: Foo = { 139 | a: 123, 140 | b: 'abc', 141 | } 142 | 143 | .. code-block:: typescript 144 | 145 | // 不要这样做! 146 | const badFoo = { 147 | a: 123, 148 | b: 'abc', 149 | } 150 | 151 | 为什么要这样做? 152 | 153 | 这是因为在上文中, ``badFoo`` 对象的类型依赖于类型推导。 ``badFoo`` 对象中可能添加额外的字段,此时类型推导的结果就有可能发生变化。 154 | 155 | 如果将 ``badFoo`` 传给接收 ``Foo`` 类型参数的函数,错误提示会出现在函数调用的位置,而非对象声明的位置。在大规模的代码仓库中修改接口时,这一点区别会很重要。 156 | 157 | .. code-block:: typescript 158 | 159 | interface Animal { 160 | sound: string; 161 | name: string; 162 | } 163 | 164 | function makeSound(animal: Animal) {} 165 | 166 | /** 167 | * 'cat' 的类型会被推导为 '{sound: string}' 168 | */ 169 | const cat = { 170 | sound: 'meow', 171 | }; 172 | 173 | /** 174 | * 'cat' 的类型并不满足函数参数的要求, 175 | * 因此 TypeScript 编译器会在这里报错, 176 | * 而这里有可能离 'cat' 的定义相当远。 177 | */ 178 | makeSound(cat); 179 | 180 | /** 181 | * Horse 具有结构类型,因此这里会提示类型错误,而函数调用点不会报错。 182 | * 这是因为 'horse' 不满足接口 'Animal' 的类型约定。 183 | */ 184 | const horse: Animal = { 185 | sound: 'niegh', 186 | }; 187 | 188 | const dog: Animal = { 189 | sound: 'bark', 190 | name: 'MrPickles', 191 | }; 192 | 193 | makeSound(dog); 194 | makeSound(horse); 195 | 196 | .. _ts-interface-vs-type-aliases: 197 | 198 | 接口 还是 类型别名? 199 | ******************************************************************************** 200 | 201 | TypeScript 支持使用 `类型别名 `_ 为类型命名。这一功能可以用于基本类型、联合类型、元组以及其它类型。 202 | 203 | 然而,当需要声明用于对象的类型时,应当使用接口,而非对象字面量表达式的类型别名。 204 | 205 | .. code-block:: typescript 206 | 207 | // 应当这样做! 208 | interface User { 209 | firstName: string; 210 | lastName: string; 211 | } 212 | 213 | .. code-block:: typescript 214 | 215 | // 不要这样做! 216 | type User = { 217 | firstName: string, 218 | lastName: string, 219 | } 220 | 221 | 为什么? 222 | 223 | 这两种形式是几乎等价的,因此,基于从两个形式中只选择其中一种以避免项目中出现变种的原则,这里选择了更常见的接口形式。另外,这里选择接口还有一个 `有趣的技术原因 `_ 。这篇博文引用了 TypeScript 团队负责人的话:“老实说,我个人的意见是对于任何可以建模的对象都应当使用接口。相比之下,使用类型别名没有任何优势,尤其是类型别名有许多的显示和性能问题”。 224 | 225 | .. _ts-array-type: 226 | 227 | ``Array`` 类型 228 | ******************************************************************************** 229 | 230 | 对于简单类型(名称中只包含字母、数字和点 ``.`` 的类型),应当使用数组的语法糖 ``T[]`` ,而非更长的 ``Array`` 形式。 231 | 232 | 对于其它复杂的类型,则应当使用较长的 ``Array`` 。 233 | 234 | 这条规则也适用于 ``readonly T[]`` 和 ``ReadonlyArray`` 。 235 | 236 | .. code-block:: typescript 237 | 238 | // 应当这样做! 239 | const a: string[]; 240 | const b: readonly string[]; 241 | const c: ns.MyObj[]; 242 | const d: Array; 243 | const e: ReadonlyArray; 244 | 245 | .. code-block:: typescript 246 | 247 | // 不要这样做! 248 | const f: Array; // 语法糖写法更短。 249 | const g: ReadonlyArray; 250 | const h: {n: number, s: string}[]; // 大括号和中括号让这行代码难以阅读。 251 | const i: (string|number)[]; 252 | const j: readonly (string|number)[]; 253 | 254 | .. _ts-indexable-type: 255 | 256 | 索引类型 ``{[key: string]: number}`` 257 | ******************************************************************************** 258 | 259 | 在 JavaScript 中,使用对象作为关联数组(又称“映射表”、“哈希表”或者“字典”)是一种常见的做法: 260 | 261 | .. code-block:: typescript 262 | 263 | const fileSizes: {[fileName: string]: number} = {}; 264 | fileSizes['readme.txt'] = 541; 265 | 266 | 在 TypeScript 中,应当为键提供一个有意义的标签名。(当然,这个标签只有在文档中有实际意义,在其它场合是无用的。) 267 | 268 | .. code-block:: typescript 269 | 270 | // 不要这样做! 271 | const users: {[key: string]: number} = ...; 272 | 273 | .. code-block:: typescript 274 | 275 | // 应当这样做! 276 | const users: {[userName: string]: number} = ...; 277 | 278 | 然而,相比使用上面的这种形式,在 TypeScript 中应当考虑使用 ES6 新增的 ``Map`` 与 ``Set`` 类型。因为 JavaScript 对象有一些 `令人困惑又不符合预期的行为 `_ ,而 ES6 的新增类型能够更明确地表达程序员的设计思路。此外, ``Map`` 类型的键和 ``Set`` 类型的元素都允许使用 ``string`` 以外的其他类型。 279 | 280 | TypeScript 内建的 ``Record`` 允许使用已定义的一组键创建类型。它与关联数组的不同之处在于键是静态确定的。关于它的使用建议,参见 :ref:`ts-mapped-conditional-types` 一节。 281 | 282 | .. _ts-mapped-conditional-types: 283 | 284 | 映射类型与条件类型 285 | ******************************************************************************** 286 | 287 | TypeScript 中的 `映射类型 `_ 与 `条件类型 `_ 让程序员能够在已有类型的基础上构建出新的类型。在 TypeScript 的标准库中有许多类型运算符都是基于这一机制(例如 ``Record`` 、 ``Partial`` 、 ``Readonly`` 等等)。 288 | 289 | TypeScript 类型系统的这一特性让创建新类型变得简洁,还程序员在设计代码抽象时,既能实现强大的功能,同时海能保证类型安全。然而,它们也有一些缺点: 290 | 291 | * 相较于显式地指定属性与类型间关系(例如使用接口和继承,参见下文中的例子),类型运算符需要读者在头脑中自行对后方的类型表达式进行求值。本质上说,这增加了程序的理解难度,尤其是在类型推导和类型表达式有可能横跨数个文件的情况下。 292 | * 映射类型与条件类型的求值模型并没有明确的规范,且经常随着 TypeScript 编译器的版本更新而发生变化,因此并不总是易于理解,尤其是与类型推导一同使用时。因此,代码有可能只是碰巧能够通过编译或者给出正确的结果。在这种情况下,使用类型运算符增加了代码未来的维护成本。 293 | * 映射类型与条件类型最为强大之处在于,它们能够从复杂且/或推导的类型中派生出新的类型。然而从另一方面看,这样做也很容易导致程序难于理解与维护。 294 | * 有些语法工具并不能够很好地支持类型系统的这一特性。例如,一些 IDE 的“查找引用”功能(以及依赖于它的“重命名重构”)无法发现位于 ``Pick`` 类型中的属性,因而在查找结果中不会将其设为高亮。 295 | 296 | 因此,推荐的代码规范如下: 297 | 298 | * 任何使用都应当使用最简单的类型构造方式进行表达。 299 | * 一定程度的重复或冗余,往往好过复杂的类型表达式带来的长远维护成本。 300 | * 映射类型和条件类型必须在符合上述理念的情况下使用。 301 | 302 | 例如,TypeScript 内建的 ``Pick`` 类型允许以类型 ``T`` 的子集创建新的类型。然而,使用接口和继承的方式实现往往更易于理解。 303 | 304 | .. code-block:: typescript 305 | 306 | interface User { 307 | shoeSize: number; 308 | favoriteIcecream: string; 309 | favoriteChocolate: string; 310 | } 311 | 312 | // FoodPreferences 类型拥有 favoriteIcecream 和 favoriteChocolate,但不包括 shoeSize。 313 | type FoodPreferences = Pick; 314 | 315 | 这种写法等价于显式地写出 ``FoodPreferences`` 的属性: 316 | 317 | .. code-block:: typescript 318 | 319 | interface FoodPreferences { 320 | favoriteIcecream: string; 321 | favoriteChocolate: string; 322 | } 323 | 324 | 为了减少重复,可以让 ``User`` 继承 ``FoodPreferences`` ,或者在 ``User`` 中嵌套一个类型为 ``FoodPrefences`` 的字段(这样做可能更好): 325 | 326 | .. code-block:: typescript 327 | 328 | interface FoodPreferences { /* 同上 */ } 329 | 330 | interface User extends FoodPreferences { 331 | shoeSize: number; 332 | // 这样 User 也包括了 FoodPreferences 的字段。 333 | } 334 | 335 | 使用接口让属性的分类变得清晰,IDE 的支持更完善,方便进一步优化,同时使得代码更易于理解。 336 | 337 | .. _ts-any-type: 338 | 339 | ``any`` 类型 340 | ******************************************************************************** 341 | 342 | TypeScript 的 ``any`` 类型是所有其它类型的超类,又是所有其它类型的子类,同时还允许解引用一切属性。因此,使用 ``any`` 十分危险——它会掩盖严重的程序错误,并且它从根本上破坏了对应的值“具有静态属性”的原则。 343 | 344 | 尽可能 *不要* 使用 ``any`` 。如果出现了需要使用 ``any`` 的场景,可以考虑下列的解决方案: 345 | 346 | * :ref:`ts-provide-a-more-specific-type` 347 | * :ref:`ts-using-unknown-over-any` 348 | * :ref:`ts-suppress-the-lint-warning` 349 | 350 | .. _ts-provide-a-more-specific-type: 351 | 352 | 提供一个更具体的类型 353 | ================================================================================ 354 | 355 | 使用接口、内联对象类型、或者类型别名: 356 | 357 | .. code-block:: typescript 358 | 359 | // 声明接口类型以表示服务端发送的 JSON。 360 | declare interface MyUserJson { 361 | name: string; 362 | email: string; 363 | } 364 | 365 | // 对重复出现的类型使用类型别名。 366 | type MyType = number|string; 367 | 368 | // 或者对复杂的返回类型使用内联对象类型。 369 | function getTwoThings(): {something: number, other: string} { 370 | // ... 371 | return {something, other}; 372 | } 373 | 374 | // 使用泛型,有些库在这种情况下可能会使用 any 表示 375 | // 这里并不考虑函数所作用于的参数类型。 376 | // 注意,对于这种写法,“只有泛型的返回类型”一节有更详细的规范。 377 | function nicestElement(items: T[]): T { 378 | // 在 items 中查找最棒的元素。 379 | // 这里还可以进一步为泛型参数 T 添加限制,例如 。 380 | } 381 | 382 | .. _ts-using-unknown-over-any: 383 | 384 | 使用 ``unknown`` 而非 ``any`` 385 | ================================================================================ 386 | 387 | ``any`` 类型的值可以赋给其它任何类型,还可以对其解引用任意属性。一般来说,这个行为不是必需的,也不符合期望,此时代码试图表达的内容其实是“该类型是未知的”。在这种情况下,应当使用内建的 ``unknown`` 类型。它能够表达相同的语义,并且,因为 ``unknown`` 不能解引用任意属性,它较 ``any`` 而言更为安全。 388 | 389 | .. code-block:: typescript 390 | 391 | // 应当这样做! 392 | // 可以将任何值(包括 null 和 undefined)赋给 val, 393 | // 但在缩窄类型或者类型转换之前并不能使用它。 394 | const val: unknown = value; 395 | 396 | .. code-block:: typescript 397 | 398 | // 不要这样做! 399 | const danger: any = value /* 这是任意一个表达式的结果 */; 400 | danger.whoops(); // 完全未经检查的访问! 401 | 402 | .. _ts-suppress-the-lint-warning: 403 | 404 | 关闭 Lint 工具对 ``any`` 的警告 405 | ================================================================================ 406 | 407 | 有时使用 ``any`` 是合理的,例如用于在测试中构造 Mock 对象。在这种情况下,应当添加注释关闭 Lint 工具对此的警告,并添加文档对使用 any 的合理性进行说明。 408 | 409 | .. code-block:: typescript 410 | 411 | // 这个测试只需要部分地实现 BookService,否则测试会失败。 412 | // 所以,这里有意地使用了一个不安全的部分实现 Mock 对象。 413 | // tslint:disable-next-line:no-any 414 | const mockBookService = ({get() { return mockBook; }} as any) as BookService; 415 | // 购物车在这个测试里并未使用。 416 | // tslint:disable-next-line:no-any 417 | const component = new MyComponent(mockBookService, /* unused ShoppingCart */ null as any); 418 | 419 | .. _ts-tuple-types: 420 | 421 | 元组类型 422 | ******************************************************************************** 423 | 424 | 应当使用元组类型代替常见的 ``Pair`` 类型的写法: 425 | 426 | .. code-block:: typescript 427 | 428 | // 不要这样做! 429 | interface Pair { 430 | first: string; 431 | second: string; 432 | } 433 | 434 | function splitInHalf(input: string): Pair { 435 | // ... 436 | return {first: x, second: y}; 437 | } 438 | 439 | .. code-block:: typescript 440 | 441 | // 应当这样做! 442 | function splitInHalf(input: string): [string, string] { 443 | // ... 444 | return [x, y]; 445 | } 446 | 447 | // 这样使用: 448 | const [leftHalf, rightHalf] = splitInHalf('my string'); 449 | 450 | 然而通常情况下,为属性提供一个有意义的名称往往能让代码更加清晰。 451 | 452 | 如果为此声明一个接口过于繁重的话,可以使用内联对象字面量类型: 453 | 454 | .. code-block:: typescript 455 | 456 | function splitHostPort(address: string): {host: string, port: number} { 457 | // ... 458 | } 459 | 460 | // 这样使用: 461 | const address = splitHostPort(userAddress); 462 | use(address.port); 463 | 464 | // 也可以使用解构进行形如元组的操作: 465 | const {host, port} = splitHostPort(userAddress); 466 | 467 | .. _ts-wrapper-types: 468 | 469 | 包装类型 470 | ******************************************************************************** 471 | 472 | 不要使用如下几种类型,它们是 JavaScript 中基本类型的包装类型: 473 | 474 | * ``String`` 、 ``Boolean`` 和 ``Number`` 。它们的含义和对应的基本类型 ``string`` 、 ``boolean`` 和 ``number`` 略有不同。任何时候,都应当使用后者。 475 | * ``Object`` 。它和 ``{}`` 与 ``object`` 类似,但包含的范围略微更大。应当使用 ``{}`` 表示“包括除 ``null`` 和 ``undefined`` 之外所有类型”的类型,使用 ``object`` 表示“所有基本类型以外”的类型(这里的“所有基本类型”包括上文中提到的基本类型, ``symbol`` 和 ``bigint`` )。 476 | 477 | 此外,不要将包装类型用作构造函数。 478 | 479 | .. _ts-return-type-only-generics: 480 | 481 | 只有泛型的返回类型 482 | ******************************************************************************** 483 | 484 | 不要创建返回类型只有泛型的 API。如果现有的 API 中存在这种情况,使用时应当显式地标明泛型参数类型。 485 | 486 | --------------------------------------------------------------------------------