Merge pull request #60 from lilinsanity/master

lilinsanity: 对第 9, 10, 11 章(原第 8, 9, 10 章)进行了一些修改
This commit is contained in:
Yang.Y 2017-04-19 17:59:30 +08:00 committed by GitHub
commit 7cc42d688b
5 changed files with 282 additions and 243 deletions

3
.gitignore vendored
View File

@ -1,3 +1,6 @@
# JetBrains Webstorm Config Files
.idea/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]

View File

@ -0,0 +1,7 @@
# lilinsanity 的改动说明
我对 Google C++ Style Guide 又做了一次修改, 主要修改了这些地方:
1. 调整了章节, 增加了新的 **函数** 一节.
2. 统一标点符号为英文标点符号.
3. 调整了排版, 在每个小节底下用加粗字体分段为"总述""优点""缺点""结论"等. 其中"总述"为原来的 TIPS, 我认为这样的排版显得有些混乱, 不够美观, 因此改成了和"优点""缺点"等并列的一段.

View File

@ -1,12 +1,10 @@
10. 结束语
11. 结束语
~~~~~~~~~~~~~~~~
.. tip::
运用常识和判断力, 并且 *保持一致*.
运用常识和判断力, 并 *保持一致*.
编辑代码时, 花点时间看看项目中的其它代码, 并熟悉其风格. 如果其它代码中 ``if`` 语句使用空格, 那么你也要使用. 如果其中的注释用星号 (*) 围成一个盒子状, 那么你同样要这么做.
编辑代码时, 花点时间看看项目中的其它代码, 并熟悉其风格. 如果其它代码中 ``if`` 语句使用空格, 那么你也要使用. 如果其中的注释用星号 (*) 围成一个盒子状, 你同样要这么做.
风格指南的重点在于提供一个通用的编程规范, 这样大家可以把精力集中在实现内容而不是表现形式上. 我们展示了全局的风格规范, 但局部风格也很重要, 如果你在一个文件中新加的代码和原有代码风格相去甚远, 这就破坏了文件本身的整体美观, 也影响阅读, 所以要尽量避免.
风格指南的重点在于提供一个通用的编程规范, 这样大家可以把精力集中在实现内容而不是表现形式上. 我们展示的是一个总体的的风格规范, 但局部风格也很重要, 如果你在一个文件中新加的代码和原有代码风格相去甚远, 这就破坏了文件本身的整体美观, 也让打乱读者在阅读代码时的节奏, 所以要尽量避免.
好了, 关于编码风格写的够多了; 代码本身才更有趣. 尽情享受吧!

View File

@ -1,45 +1,49 @@
9. 规则特例
10. 规则特例
------------------
前面说明的编程习惯基本都是强制性的. 但所有优秀的规则都允许例外, 这里就是探讨这些特例.
9.1. 现有不合规范的代码
10.1. 现有不合规范的代码
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
**总述**
对于现有不符合既定编程风格的代码可以网开一面.
对于现有不符合既定编程风格的代码可以网开一面.
当你修改使用其他风格的代码时, 为了与代码原有风格保持一致可以不使用本指南约定. 如果不放心可以与代码原作者或现在的负责人员商讨, 记住, *一致性* 包括原有的一致性.
**说明**
当你修改使用其他风格的代码时, 为了与代码原有风格保持一致可以不使用本指南约定. 如果不放心, 可以与代码原作者或现在的负责人员商讨. 记住, *一致性* 也包括原有的一致性.
.. _windows-code:
9.2. Windows 代码
10.2. Windows 代码
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
**总述**
Windows 程序员有自己的编程习惯, 主要源于 Windows 头文件和其它 Microsoft 代码. 我们希望任何人都可以顺利读懂你的代码, 所以针对所有平台的 C++ 编程只给出一个单独的指南.
Windows 程序员有自己的编程习惯, 主要源于 Windows 头文件和其它 Microsoft 代码. 我们希望任何人都可以顺利读懂你的代码, 所以针对所有平台的 C++ 编程只给出一个单独的指南.
**说明**
如果你习惯使用 Windows 编码风格, 这儿有必要重申一下某些你可能会忘记的指南:
- 不要使用匈牙利命名法 (比如把整型变量命名成 ``iNum``). 使用 Google 命名约定, 包括对源文件使用 ``.cc`` 扩展名.
- 不要使用匈牙利命名法 (比如把整型变量命名成 ``iNum``). 使用 Google 命名约定, 包括对源文件使用 ``.cc`` 扩展名.
- Windows 定义了很多原生类型的同义词 (YuleFox 注: 这一点, 我也很反感), 如 ``DWORD``, ``HANDLE`` 等等. 在调用 Windows API 时这是完全可以接受甚至鼓励的. 但还是尽量使用原有的 C++ 类型, 例如, 使用 ``const TCHAR *`` 而不是 ``LPCTSTR``.
- Windows 定义了很多原生类型的同义词 (YuleFox 注: 这一点, 我也很反感), 如 ``DWORD``, ``HANDLE`` 等等. 在调用 Windows API 时这是完全可以接受甚至鼓励的. 即使如此, 还是尽量使用原有的 C++ 类型, 例如使用 ``const TCHAR *`` 而不是 ``LPCTSTR``.
- 使用 Microsoft Visual C++ 进行编译时, 将警告级别设置为 3 或更高, 并将所有 warnings 当作 errors 处理.
- 使用 Microsoft Visual C++ 进行编译时, 将警告级别设置为 3 或更高, 并将所有警告(warnings)当作错误(errors)处理.
- 不要使用 ``#pragma once``; 而应该使用 Google 的头文件保护规则. 头文件保护的路径应该相对于项目根目录 (Yang.Y 注: 如 ``#ifndef SRC_DIR_BAR_H_``, 参考 :ref:`#define 保护 <define_guard>` 一节).
- 不要使用 ``#pragma once``; 而应该使用 Google 的头文件保护规则. 头文件保护的路径应该相对于项目根目录 (Yang.Y 注: 如 ``#ifndef SRC_DIR_BAR_H_``, 参考 :ref:`#define 保护 <define-guard>` 一节).
- 除非万不得已, 不要使用任何非标准的扩展, 如 ``#pragma````__declspec``. 允许使用 ``__declspec(dllimport)````__declspec(dllexport)``; 但你必须通过宏来使用, 比如 ``DLLIMPORT````DLLEXPORT``, 这样其他人在分享使用这些代码时很容易就去掉这些扩展.
- 除非万不得已, 不要使用任何非标准的扩展, 如 ``#pragma````__declspec``. 使用 ``__declspec(dllimport)````__declspec(dllexport)`` 是允许的, 但必须通过宏来使用, 比如 ``DLLIMPORT````DLLEXPORT``, 这样其他人在分享使用这些代码时可以很容易地禁用这些扩展.
在 Windows 上, 只有很少的一些情况下, 我们可以偶尔违反规则:
然而, 在 Windows 上仍然有一些我们偶尔需要违反的规则:
- 通常我们 :ref:`禁止使用多重继承 <multiple-inheritance>`, 但在使用 COM 和 ATL/WTL 类时可以使用多重继承. 为了实现 COM 或 ATL/WTL 类/接口, 你可能不得不使用多重实现继承.
- 通常我们 :ref:`禁止使用多重继承 <multiple-inheritance>`, 但在使用 COM 和 ATL/WTL 类时可以使用多重继承. 为了实现 COM 或 ATL/WTL 类/接口, 你可能不得不使用多重实现继承.
- 虽然代码中不应该使用异常, 但是在 ATL 和部分 STL包括 Visual C++ 的 STL) 中异常被广泛使用. 使用 ATL 时, 应定义 ``_ATL_NO_EXCEPTIONS`` 以禁用异常. 你要研究一下是否能够禁用 STL 的异常, 如果无法禁用, 启用编译器异常也可以. (注意这只是为了编译 STL, 自己代码里仍然不要含异常处理.)
- 虽然代码中不应该使用异常, 但是在 ATL 和部分 STL包括 Visual C++ 的 STL) 中异常被广泛使用. 使用 ATL 时, 应定义 ``_ATL_NO_EXCEPTIONS`` 以禁用异常. 你要研究一下是否能够禁用 STL 的异常, 如果无法禁用, 可以启用编译器异常. (注意这只是为了编译 STL, 自己的代码里仍然不应当包含异常处理).
- 通常为了利用头文件预编译, 每个每个源文件的开头都会包含一个名为 ``StdAfx.h````precompile.h`` 的文件. 为了使代码方便与其他项目共享, 避免显式包含此文件 (``precompile.cc``), 使用 ``/FI`` 编译器选项以自动包含.
- 通常为了利用头文件预编译, 每个每个源文件的开头都会包含一个名为 ``StdAfx.h````precompile.h`` 的文件. 为了使代码方便与其他项目共享, 避免显式包含此文件 (除了在 ``precompile.cc``), 使用 ``/FI`` 编译器选项以自动包含该文件.
- 资源头文件通常命名为 ``resource.h``, 且只包含宏, 不需要遵守本风格指南.
- 资源头文件通常命名为 ``resource.h`` 且只包含宏, 这一文件不需要遵守本风格指南.

View File

@ -1,259 +1,286 @@
8. 格式
9. 格式
------------
代码风格和格式确实比较随意, 但一个项目中所有人遵循同一风格是非常容易的. 个体未必同意下述每一处格式规则, 但整个项目服从统一的编程风格是很重要的, 只有这样才能让所有人能很轻松的阅读和理解代码.
每个人都可能有自己的代码风格和格式, 但如果一个项目中的所有人都遵循同一风格的话, 这个项目就能更顺利地进行. 每个人未必能同意下述的每一处格式规则, 而且其中的不少规则需要一定时间的适应, 但整个项目服从统一的编程风格是很重要的, 只有这样才能让所有人轻松地阅读和理解代码.
另外, 我们写了一个 `emacs 配置文件 <http://google-styleguide.googlecode.com/svn/trunk/google-c-style.el>`_ 来帮助你正确的格式化代码.
为了帮助你正确的格式化代码, 我们写了一个 `emacs 配置文件 <http://google-styleguide.googlecode.com/svn/trunk/google-c-style.el>`_.
.. _line-length:
8.1. 行长度
9.1. 行长度
~~~~~~~~~~~~~~~~~~~~
.. tip::
**总述**
每一行代码字符数不超过 80.
每一行代码字符数不超过 80.
我们也认识到这条规则是有争议的, 但很多已有代码都已经遵照这一规则, 我们感觉一致性更重要.
我们也认识到这条规则是有争议的, 但很多已有代码都遵照这一规则, 因此我们感觉一致性更重要.
优点:
**优点**
提倡该原则的人主张强迫他们调整编辑器窗口大小很野蛮. 很多人同时并排开几个代码窗口, 根本没有多余空间拉伸窗口. 大家都把窗口最大尺寸加以限定, 并且 80 列宽是传统标准. 为什么要改变呢?
提倡该原则的人认为强迫他们调整编辑器窗口大小是很野蛮的行为. 很多人同时并排开几个代码窗口, 根本没有多余空间拉伸窗口. 大家都把窗口最大尺寸加以限定, 并且 80 列宽是传统标准. 那么为什么要改变呢?
缺点:
**缺点**
反对该原则的人则认为更宽的代码行更易阅读. 80 列的限制是上个世纪 60 年代的大型机的古板缺陷; 现代设备具有更宽的显示屏, 很轻松的可以显示更多代码.
反对该原则的人则认为更宽的代码行更易阅读. 80 列的限制是上个世纪 60 年代的大型机的古板缺陷; 现代设备具有更宽的显示屏, 可以很轻松地显示更多代码.
结论:
**结论**
80 个字符是最大值.
80 个字符是最大值.
特例:
如果无法在不伤害易读性的条件下进行断行, 那么注释行可以超过 80 个字符, 这样可以方便复制粘贴. 例如, 带有命令示例或 URL 的行可以超过 80 个字符.
- 如果一行注释包含了超过 80 字符的命令或 URL, 出于复制粘贴的方便允许该行超过 80 字符.
- 包含长路径的 ``#include`` 语句可以超出80列. 但应该尽量避免.
- :ref:`头文件保护 <define-guard>` 可以无视该原则.
包含长路径的 ``#include`` 语句可以超出80列.
8.2. 非 ASCII 字符
:ref:`头文件保护 <define-guard>` 可以无视该原则.
9.2. 非 ASCII 字符
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
**总述**
尽量不使用非 ASCII 字符, 使用时必须使用 UTF-8 编码.
尽量不使用非 ASCII 字符, 使用时必须使用 UTF-8 编码.
即使是英文, 也不应将用户界面的文本硬编码到源代码中, 因此非 ASCII 字符要少用. 特殊情况下可以适当包含此类字符. 如, 代码分析外部数据文件时, 可以适当硬编码数据文件中作为分隔符的非 ASCII 字符串; 更常见的是 (不需要本地化的) 单元测试代码可能包含非 ASCII 字符串. 此类情况下, 应使用 UTF-8 编码, 因为很多工具都可以理解和处理 UTF-8 编码.
**说明**
十六进制编码也可以, 能增强可读性的情况下尤其鼓励 —— 比如 ``"\xEF\xBB\xBF"`` 在 Unicode 中是 *零宽度 无间断* 的间隔符号, 如果不用十六进制直接放在 UTF-8 格式的源文件中, 是看不到的.
即使是英文, 也不应将用户界面的文本硬编码到源代码中, 因此非 ASCII 字符应当很少被用到. 特殊情况下可以适当包含此类字符. 例如, 代码分析外部数据文件时, 可以适当硬编码数据文件中作为分隔符的非 ASCII 字符串; 更常见的是 (不需要本地化的) 单元测试代码可能包含非 ASCII 字符串. 此类情况下, 应使用 UTF-8 编码, 因为很多工具都可以理解和处理 UTF-8 编码.
十六进制编码也可以, 能增强可读性的情况下尤其鼓励 —— 比如 ``"\xEF\xBB\xBF"``, 或者更简洁地写作 ``u8"\uFEFF"``, 在 Unicode 中是 *零宽度 无间断* 的间隔符号, 如果不用十六进制直接放在 UTF-8 格式的源文件中, 是看不到的.
(Yang.Y 注: ``"\xEF\xBB\xBF"`` 通常用作 UTF-8 with BOM 编码标记)
``u8`` 前缀把带 ``uXXXX`` 转义序列的字符串字面值编码成 UTF-8. 不要用在本身就带 UTF-8 字符的字符串字面值上,因为如果编译器不把源代码识别成 UTF-8, 输出就会出错。
使``u8`` 前缀把带 ``uXXXX`` 转义序列的字符串字面值编码成 UTF-8. 不要用在本身就带 UTF-8 字符的字符串字面值上, 因为如果编译器不把源代码识别成 UTF-8, 输出就会出错.
别用 C++11 的 ``char16_t````char32_t``, 它们和 UTF-8 文本没有关系``wchar_t`` 同理,除非您写的代码要调用 Windows API, 后者有用到 ``wchar_t`` 扩展。
别用 C++11 的 ``char16_t````char32_t``, 它们和 UTF-8 文本没有关系, ``wchar_t`` 同理, 除非你写的代码要调用 Windows API, 后者广泛使用了 ``wchar_t``.
8.3. 空格还是制表位
9.3. 空格还是制表位
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
**总述**
只使用空格, 每次缩进 2 个空格.
只使用空格, 每次缩进 2 个空格.
我们使用空格缩进. 不要在代码中使用制符表. 你应该设置编辑器将制符表转为空格.
**说明**
8.4. 函数声明与定义
我们使用空格缩进. 不要在代码中使用制表符. 你应该设置编辑器将制表符转为空格.
9.4. 函数声明与定义
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
**总述**
返回类型和函数名在同一行, 参数也尽量放在同一行,如果放不下就对形参分行。
返回类型和函数名在同一行, 参数也尽量放在同一行, 如果放不下就对形参分行, 分行方式与 :ref:`函数调用 <function-calls>` 一致.
**说明**
函数看上去像这样:
.. code-block:: c++
.. code-block:: c++
ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {
DoSomething();
...
}
ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {
DoSomething();
...
}
如果同一行文本太多, 放不下所有参数:
.. code-block:: c++
.. code-block:: c++
ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2,
Type par_name3) {
DoSomething();
...
}
ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2,
Type par_name3) {
DoSomething();
...
}
甚至连第一个参数都放不下:
.. code-block:: c++
.. code-block:: c++
ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
Type par_name1, // 4 空格缩进
Type par_name2,
Type par_name3) {
DoSomething(); // 2 空格缩进
...
}
ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
Type par_name1, // 4 space indent
Type par_name2,
Type par_name3) {
DoSomething(); // 2 space indent
...
}
注意以下几点:
- 如果返回类型和函数名在一行放不下,分行。
- 使用好的参数名.
- 如果返回类型那个与函数声明或定义分行了,不要缩进。
- 只有在参数未被使用或者其用途非常明显时, 才能省略参数名.
- 左圆括号总是和函数名在同一行;
- 如果返回类型和函数名在一行放不下, 分行.
- 函数名和左圆括号间没有空格;
- 如果返回类型与函数声明或定义分行了, 不要缩进.
- 圆括号与参数间没有空格;
- 左圆括号总是和函数名在同一行.
- 左大括号总在最后一个参数同一行的末尾处;
- 函数名和左圆括号间永远没有空格.
- 如果其它风格规则允许的话,右大括号总是单独位于函数最后一行,或者与左大括号同一行。
- 圆括号与参数间没有空格.
- 右大括号和左大括号间总是有一个空格;
- 左大括号总在最后一个参数同一行的末尾处, 不另起新行.
- 函数声明和定义中的所有形参必须有命名且一致;
- 右大括号总是单独位于函数最后一行, 或者与左大括号同一行.
- 所有形参应尽可能对齐;
- 右圆括号和左大括号间总是有一个空格.
- 缺省缩进为 2 个空格;
- 所有形参应尽可能对齐.
- 换行后的参数保持 4 个空格的缩进;
- 缺省缩进为 2 个空格.
如果有些参数没有用到, 在函数定义处将参数名注释起来:
- 换行后的参数保持 4 个空格的缩进.
.. code-block:: c++
未被使用的参数, 或者根据上下文很容易看出其用途的参数, 可以省略参数名:
// 接口中形参恒有命名。
class Shape {
public:
virtual void Rotate(double radians) = 0;
}
.. code-block:: c++
// 声明中形参恒有命名。
class Circle : public Shape {
public:
virtual void Rotate(double radians);
}
class Foo {
public:
Foo(Foo&&);
Foo(const Foo&);
Foo& operator=(Foo&&);
Foo& operator=(const Foo&);
};
// 定义中注释掉无用变量。
void Circle::Rotate(double /*radians*/) {}
未被使用的参数如果其用途不明显的话, 在函数定义处将参数名注释起来:
.. warning::
.. code-block:: c++
.. code-block:: c++
class Shape {
public:
virtual void Rotate(double radians) = 0;
};
// 差 - 如果将来有人要实现,很难猜出变量是干什么用的。
void Circle::Rotate(double) {}
class Circle : public Shape {
public:
void Rotate(double radians) override;
};
8.5. Lambda 表达式
void Circle::Rotate(double /*radians*/) {}
.. code-block:: c++
// 差 - 如果将来有人要实现, 很难猜出变量的作用.
void Circle::Rotate(double) {}
属性, 和展开为属性的宏, 写在函数声明或定义的最前面, 即返回类型之前:
.. code-block:: c++
MUST_USE_RESULT bool IsOK();
9.5. Lambda 表达式
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
**总述**
其它函数怎么格式化形参和函数体Lambda 表达式就怎么格式化;捕获列表同理。
Lambda 表达式对形参和函数体的格式化和其他函数一致; 捕获列表同理, 表项用逗号隔开.
若用引用捕获,在变量名和 ``&`` 之间不留空格。
**说明**
.. code-block:: c++
若用引用捕获, 在变量名和 ``&`` 之间不留空格.
int x = 0;
auto add_to_x = [&x](int n) { x += n; };
.. code-block:: c++
短 lambda 就写得和内联函数一样。
int x = 0;
auto add_to_x = [&x](int n) { x += n; };
.. code-block:: c++
短 lambda 就写得和内联函数一样.
std::set<int> blacklist = {7, 8, 9};
std::vector<int> digits = {3, 9, 1, 8, 4, 7, 1};
digits.erase(std::remove_if(digits.begin(), digits.end(), [&blacklist](int i) {
return blacklist.find(i) != blacklist.end();
}),
digits.end());
.. code-block:: c++
8.6. 函数调用
std::set<int> blacklist = {7, 8, 9};
std::vector<int> digits = {3, 9, 1, 8, 4, 7, 1};
digits.erase(std::remove_if(digits.begin(), digits.end(), [&blacklist](int i) {
return blacklist.find(i) != blacklist.end();
}),
digits.end());
.. _function-calls:
9.6. 函数调用
~~~~~~~~~~~~~~~~~~~~~~
.. tip::
**总述**
要么一行写完函数调用,要么在圆括号里对参数分行,要么参数另起一行且缩进四格。如果没有其它顾虑的话,尽可能精简行数,比如把多个参数适当地放在同一行里。
要么一行写完函数调用, 要么在圆括号里对参数分行, 要么参数另起一行且缩进四格. 如果没有其它顾虑的话, 尽可能精简行数, 比如把多个参数适当地放在同一行里.
函数调用遵循如下形式:
**说明**
.. code-block:: c++
函数调用遵循如下形式:
bool retval = DoSomething(argument1, argument2, argument3);
.. code-block:: c++
如果同一行放不下,可断为多行,后面每一行都和第一个实参对齐,左圆括号后和右圆括号前不要留空格:
bool retval = DoSomething(argument1, argument2, argument3);
.. code-block:: c++
如果同一行放不下, 可断为多行, 后面每一行都和第一个实参对齐, 左圆括号后和右圆括号前不要留空格:
bool retval = DoSomething(averyveryveryverylongargument1,
argument2, argument3);
.. code-block:: c++
参数也可以放在次行,缩进四格:
bool retval = DoSomething(averyveryveryverylongargument1,
argument2, argument3);
.. code-block:: c++
参数也可以放在次行, 缩进四格:
if (...) {
...
...
if (...) {
DoSomething(
argument1, argument2, // 4 空格缩进
argument3, argument4);
}
.. code-block:: c++
把多个参数放在同一行,是为了减少函数调用所需的行数,除非影响到可读性。有人认为把每个参数都独立成行,不仅更好读,而且方便编辑参数。不过,比起所谓的参数编辑,我们更看重可读性,且后者比较好办:
if (...) {
...
...
if (...) {
DoSomething(
argument1, argument2, // 4 空格缩进
argument3, argument4);
}
如果一些参数本身就是略复杂的表达式,且降低了可读性。那么可以直接创建临时变量描述该表达式,并传递给函数:
把多个参数放在同一行以减少函数调用所需的行数, 除非影响到可读性. 有人认为把每个参数都独立成行, 不仅更好读, 而且方便编辑参数. 不过, 比起所谓的参数编辑, 我们更看重可读性, 且后者比较好办
.. code-block:: c++
如果一些参数本身就是略复杂的表达式, 且降低了可读性, 那么可以直接创建临时变量描述该表达式, 并传递给函数:
int my_heuristic = scores[x] * y + bases[x];
bool retval = DoSomething(my_heuristic, x, y, z);
.. code-block:: c++
或者放着不管,补充上注释:
int my_heuristic = scores[x] * y + bases[x];
bool retval = DoSomething(my_heuristic, x, y, z);
.. code-block:: c++
或者放着不管, 补充上注释:
bool retval = DoSomething(scores[x] * y + bases[x], // Score heuristic.
x, y, z);
.. code-block:: c++
如果某参数独立成行,对可读性更有帮助的话,就这么办。
bool retval = DoSomething(scores[x] * y + bases[x], // Score heuristic.
x, y, z);
此外,如果一系列参数本身就有一定的结构,可以酌情地按其结构来决定参数格式:
如果某参数独立成行, 对可读性更有帮助的话, 那也可以如此做. 参数的格式处理应当以可读性而非其他作为最重要的原则.
.. code-block:: c++
此外, 如果一系列参数本身就有一定的结构, 可以酌情地按其结构来决定参数格式:
// 通过 3x3 矩阵转换 widget.
my_widget.Transform(x1, x2, x3,
y1, y2, y3,
z1, z2, z3);
.. code-block:: c++
// 通过 3x3 矩阵转换 widget.
my_widget.Transform(x1, x2, x3,
y1, y2, y3,
z1, z2, z3);
.. _braced-initializer-list-format
8.7. 列表初始化格式
9.7. 列表初始化格式
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
您平时怎么格式化函数调用,就怎么格式化 :ref:`braced_initializer_list`
您平时怎么格式化函数调用, 就怎么格式化 :ref:`braced_initializer_list`.
如果列表初始化伴随着名字,比如类型或变量名,您可以当名字是函数、{} 是函数调用的括号来格式化它。反之,就当它有个长度为零的名字。
如果列表初始化伴随着名字, 比如类型或变量名, 您可以当名字是函数、{} 是函数调用的括号来格式化它. 反之, 就当它有个长度为零的名字.
.. code-block:: c++
// 一行列表初始化示范
// 一行列表初始化示范.
return {foo, bar};
functioncall({foo, bar});
pair<int, int> p{foo, bar};
// 当不得不断行时
// 当不得不断行时.
SomeFunction(
{"assume a zero-length name before {"},
some_other_function_parameter);
@ -267,14 +294,14 @@
some, other, values}};
SomeType variable{
"This is too long to fit all in one line"};
MyType m = { // 注意了,您可以在 { 前断行。
MyType m = { // 注意了, 您可以在 { 前断行.
superlongvariablename1,
superlongvariablename2,
{short, interior, list},
{interiorwrappinglist,
interiorwrappinglist2}};
8.8. 条件语句
9.9. 条件语句
~~~~~~~~~~~~~~~~~~~~~~
.. tip::
@ -287,9 +314,9 @@
.. code-block:: c++
if (condition) { 圆括号里没空格紧邻
... // 2 空格缩进
} else { // else 与 if 的右括号同一行
if (condition) { 圆括号里没空格紧邻.
... // 2 空格缩进.
} else { // else 与 if 的右括号同一行.
...
}
@ -298,8 +325,8 @@
.. code-block:: c++
if ( condition ) { // 圆括号与空格紧邻 - 不常见
... // 2 空格缩进
} else { // else 与 if 的右括号同一行
... // 2 空格缩进.
} else { // else 与 if 的右括号同一行.
...
}
@ -309,13 +336,13 @@
.. code-block:: c++
if(condition) // 差 - IF 后面没空格
if (condition){ // 差 - { 前面没空格
if(condition){ // 变本加厉地差
if(condition) // 差 - IF 后面没空格.
if (condition){ // 差 - { 前面没空格.
if(condition){ // 变本加厉地差.
.. code-block:: c++
if (condition) { // 可 - IF 和 { 都与空格紧邻
if (condition) { // 可 - IF 和 { 都与空格紧邻.
如果能增强可读性, 简短的条件语句允许写在同一行. 只有当语句简单并且没有使用 ``else`` 子句时使用:
@ -339,10 +366,10 @@
.. code-block:: c++
if (condition)
DoSomething(); // 2 空格缩进
DoSomething(); // 2 空格缩进.
if (condition) {
DoSomething(); // 2 空格缩进
DoSomething(); // 2 空格缩进.
}
但如果语句中某个 ``if-else`` 分支使用了大括号的话, 其它分支也必须使用:
@ -351,13 +378,13 @@
.. code-block:: c++
// 不可以这样子 - IF 有大括号 ELSE 却没有
// 不可以这样子 - IF 有大括号 ELSE 却没有.
if (condition) {
foo;
} else
bar;
// 不可以这样子 - ELSE 有大括号 IF 却没有
// 不可以这样子 - ELSE 有大括号 IF 却没有.
if (condition)
foo;
else {
@ -367,19 +394,19 @@
.. code-block:: c++
// 只要其中一个分支用了大括号,两个分支都要用上大括号。
// 只要其中一个分支用了大括号, 两个分支都要用上大括号.
if (condition) {
foo;
} else {
bar;
}
8.9. 循环和开关选择语句
9.9. 循环和开关选择语句
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
``switch`` 语句可以使用大括号分段,以表明 cases 之间不是连在一起的。在单语句循环里,括号可用可不用。空循环体应使用 ``{}````continue``.
``switch`` 语句可以使用大括号分段, 以表明 cases 之间不是连在一起的. 在单语句循环里, 括号可用可不用. 空循环体应使用 ``{}````continue``.
``switch`` 语句中的 ``case`` 块可以使用大括号也可以不用, 取决于你的个人喜好. 如果用的话, 要按照下文所述的方法.
@ -401,7 +428,7 @@
}
}
在单语句循环里括号可用可不用:
在单语句循环里, 括号可用可不用:
.. code-block:: c++
@ -417,18 +444,18 @@
.. code-block:: c++
while (condition) {
// 反复循环直到条件失效
// 反复循环直到条件失效.
}
for (int i = 0; i < kSomeNumber; ++i) {} // 可 - 空循环体
while (condition) continue; // 可 - contunue 表明没有逻辑
for (int i = 0; i < kSomeNumber; ++i) {} // 可 - 空循环体.
while (condition) continue; // 可 - contunue 表明没有逻辑.
.. warning::
.. code-block:: c++
while (condition); // 差 - 看起来仅仅只是 while/loop 的部分之一
while (condition); // 差 - 看起来仅仅只是 while/loop 的部分之一.
8.10. 指针和引用表达式
9.10. 指针和引用表达式
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
@ -452,11 +479,11 @@
.. code-block:: c++
// 好样的,空格前置。
// 好样的, 空格前置.
char *c;
const string &str;
// 好样的,空格后置。
// 好样的, 空格后置.
char* c; // 但别忘了 "char* c, *d, *e, ...;"!
const string& str;
@ -465,11 +492,11 @@
.. code-block:: c++
char * c; // 差 - * 两边都有空格
const string & str; // 差 - & 两边都有空格
const string & str; // 差 - & 两边都有空格.
在单个文件内要保持风格一致, 所以, 如果是修改现有文件, 要遵照该文件的风格.
8.11. 布尔表达式
9.11. 布尔表达式
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
@ -486,23 +513,23 @@
...
}
注意, 上例的逻辑与 (``&&``) 操作符均位于行尾. 这格式在 Google 里很常见,您要把所有操作符放在开头也可以。可以考虑额外插入圆括号, 合理使用的话对增强可读性是很有帮助的. 此外直接用符号形式的操作符比如 ``&&````~``, 不要用词语形式的 ``and````compl``.
注意, 上例的逻辑与 (``&&``) 操作符均位于行尾. 这格式在 Google 里很常见, 您要把所有操作符放在开头也可以. 可以考虑额外插入圆括号, 合理使用的话对增强可读性是很有帮助的. 此外直接用符号形式的操作符, 比如 ``&&````~``, 不要用词语形式的 ``and````compl``.
8.12. 函数返回值
9.12. 函数返回值
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
``return`` 表达式里时没必要都用圆括号
``return`` 表达式里时没必要都用圆括号.
假如您写 ``x = epr`` 时本来就会加上括号,那 ``return expr;`` 也可如法炮制。
假如您写 ``x = epr`` 时本来就会加上括号, 那 ``return expr;`` 也可如法炮制.
函数返回时不要使用圆括号:
.. code-block:: c++
return result; // 返回值很简单,没有圆括号。
// 可以用圆括号把复杂表达式圈起来,改善可读性。
return result; // 返回值很简单, 没有圆括号.
// 可以用圆括号把复杂表达式圈起来, 改善可读性.
return (some_long_condition &&
another_condition);
@ -513,7 +540,7 @@
return (value); // 毕竟您从来不会写 var = (value);
return(result); // return 可不是函数!
8.13. 变量及数组初始化
9.13. 变量及数组初始化
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
@ -531,21 +558,21 @@
string name = "Some Name";
string name{"Some Name"};
请务必小心列表初始化 {...} 用 ``std::initializer_list`` 构造函数初始化出的类型非空列表初始化就会优先调用 ``std::initializer_list``, 不过空列表初始化除外,后者原则上会调用默认构造函数。为了强制禁用 ``std::initializer_list`` 构造函数,请改用括号。
请务必小心列表初始化 {...} 用 ``std::initializer_list`` 构造函数初始化出的类型. 非空列表初始化就会优先调用 ``std::initializer_list``, 不过空列表初始化除外, 后者原则上会调用默认构造函数. 为了强制禁用 ``std::initializer_list`` 构造函数, 请改用括号.
.. code-block:: c++
vector<int> v(100, 1); // A vector of 100 1s.
vector<int> v{100, 1}; // A vector of 100, 1.
此外,列表初始化不允许整型类型的四舍五入,这可以用来避免一些类型上的编程失误。
此外, 列表初始化不允许整型类型的四舍五入, 这可以用来避免一些类型上的编程失误.
.. code-block:: c++
int pi(3.14); // 可 -- pi == 3.
int pi{3.14}; // Compile error: narrowing conversion.
8.14. 预处理指令
9.14. 预处理指令
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
@ -558,7 +585,7 @@
// 可 - directives at beginning of line
if (lopsided_score) {
#if DISASTER_PENDING // 正确 -- 行开头起
#if DISASTER_PENDING // 正确 -- 行开头起.
DropEverything();
#endif
BackToNormal();
@ -576,7 +603,7 @@
BackToNormal();
}
8.15. 类格式
9.15. 类格式
~~~~~~~~~~~~~~~~~~~~~~
.. tip::
@ -589,7 +616,7 @@
class MyClass : public OtherClass {
public: // 注意有 1 空格缩进!
MyClass(); // 照常2 空格缩进。
MyClass(); // 照常, 2 空格缩进.
explicit MyClass(int var);
~MyClass() {}
@ -622,7 +649,7 @@
- 关于声明顺序的规则请参考 :ref:`声明顺序 <declaration-order>` 一节.
8.16. 构造函数初始值列表
9.16. 构造函数初始值列表
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
@ -640,7 +667,7 @@
.. code-block:: c++
// 如果要断成多行,缩进四格,冒号放在第一行初始化句:
// 如果要断成多行, 缩进四格, 冒号放在第一行初始化句:
MyClass::MyClass(int var)
: some_var_(var), // 4 空格缩进
some_other_var_(var + 1) { // 对准
@ -649,7 +676,7 @@
...
}
8.17. 名字空间格式化
9.17. 名字空间格式化
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
@ -662,7 +689,7 @@
namespace {
void foo() { // 正确。命名空间内没有额外的缩进。
void foo() { // 正确. 命名空间内没有额外的缩进.
...
}
@ -676,21 +703,21 @@
namespace {
// 错,缩进多余了。
// 错, 缩进多余了.
void foo() {
...
}
} // namespace
声明嵌套命名空间时,每命名空间都独立成行。
声明嵌套命名空间时, 每命名空间都独立成行.
.. code-block:: c++
namespace foo {
namespace bar {
8.18. 水平留白
9.19. 水平留白
~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
@ -701,17 +728,17 @@
.. code-block:: c++
void f(bool b) { // 左大括号前恒有空格
void f(bool b) { // 左大括号前恒有空格.
...
int i = 0; // 分号前不加空格
int x[] = { 0 }; // 大括号内部可与空格紧邻也不可,不过两边都要加上。
int i = 0; // 分号前不加空格.
int x[] = { 0 }; // 大括号内部可与空格紧邻也不可, 不过两边都要加上.
int x[] = {0};
// 继承与初始化列表中的冒号前后恒有空格
// 继承与初始化列表中的冒号前后恒有空格.
class Foo : public Bar {
public:
// 至于内联函数实现,在大括号内部加上空格并编写实现。
Foo(int b) : Bar(), baz_(b) {} // 大括号里面是空的话,不加空格。
void Reset() { baz_ = 0; } // 用括号把大括号与实现分开
// 至于内联函数实现, 在大括号内部加上空格并编写实现.
Foo(int b) : Bar(), baz_(b) {} // 大括号里面是空的话, 不加空格.
void Reset() { baz_ = 0; } // 用括号把大括号与实现分开.
...
添加冗余的留白会给其他人编辑时造成额外负担. 因此, 行尾不要留空格. 如果确定一行代码已经修改完毕, 将多余的空格去掉; 或者在专门清理空格时去掉(确信没有其他人在处理). (Yang.Y 注: 现在大部分代码编辑器稍加设置后, 都支持自动删除行首/行尾空格, 如果不支持, 考虑换一款编辑器或 IDE)
@ -720,35 +747,35 @@
.. code-block:: c++
if (b) { // if 条件语句和循环语句关键字后均有空格
} else { // else 前后有空格
if (b) { // if 条件语句和循环语句关键字后均有空格.
} else { // else 前后有空格.
}
while (test) {} // 圆括号内部不紧邻空格
while (test) {} // 圆括号内部不紧邻空格.
switch (i) {
for (int i = 0; i < 5; ++i) {
switch ( i ) { // 循环和条件语句的圆括号里可以与空格紧邻
if ( test ) { // 圆括号,但这很少见。总之要一致。
switch ( i ) { // 循环和条件语句的圆括号里可以与空格紧邻.
if ( test ) { // 圆括号, 但这很少见. 总之要一致.
for ( int i = 0; i < 5; ++i ) {
for ( ; i < 5 ; ++i) { // 循环里内 ; 后恒有空格 前可以加个空格。
for ( ; i < 5 ; ++i) { // 循环里内 ; 后恒有空格, ; 前可以加个空格.
switch (i) {
case 1: // switch case 的冒号前无空格
case 1: // switch case 的冒号前无空格.
...
case 2: break; // 如果冒号有代码,加个空格。
case 2: break; // 如果冒号有代码, 加个空格.
操作符:
.. code-block:: c++
// 赋值操作系统前后恒有空格
// 赋值操作系统前后恒有空格.
x = 0;
// 其它二元操作符也前后恒有空格,不过对 factors 前后不加空格也可以。
// 圆括号内部不紧邻空格
// 其它二元操作符也前后恒有空格, 不过对 factors 前后不加空格也可以.
// 圆括号内部不紧邻空格.
v = w * x + y / z;
v = w*x + y/z;
v = w * (x + z);
// 在参数和一元操作符之间不加空格
// 在参数和一元操作符之间不加空格.
x = -5;
++x;
if (x && !y)
@ -758,19 +785,19 @@
.. code-block:: c++
// 尖叫括号(< and >) 不与空格紧邻< 前没有空格,>( 之间也没有。
// 尖叫括号(< and >) 不与空格紧邻, < 前没有空格, >( 之间也没有.
vector<string> x;
y = static_cast<char*>(x);
// 在类型与指针操作符之间留空格也可以,但要保持一致。
// 在类型与指针操作符之间留空格也可以, 但要保持一致.
vector<char *> x;
set<list<string>> x; // 在 C++11 代码里可以这样用了
set<list<string> > x; // C++03 中要在 > > 里留个空格
set<list<string>> x; // 在 C++11 代码里可以这样用了.
set<list<string> > x; // C++03 中要在 > > 里留个空格.
// 您或许可以在 < < 里加上一对对称的空格
// 您或许可以在 < < 里加上一对对称的空格.
set< list<string> > x;
8.19. 垂直留白
9.19. 垂直留白
~~~~~~~~~~~~~~~~~~~~~~~~
.. tip::
@ -783,8 +810,8 @@
空行心得如下:
* 函数体内开头或结尾的空行可读性微乎其微
* 在多重 if-else 块里加空行或许有点可读性
* 函数体内开头或结尾的空行可读性微乎其微.
* 在多重 if-else 块里加空行或许有点可读性.
译者 (YuleFox) 笔记
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -805,11 +832,11 @@
译者acgtyrant笔记
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#. 80 行限制事实上有助于避免代码可读性失控,比如超多重嵌套块,超多重函数调用等等。
#. Linux 上设置好了 Locale 就几乎一劳永逸设置好所有开发环境的编码不像奇葩的 Windows.
#. Google 强调有一对 if-else 时,不论有没有嵌套,都要有大括号。Apple 正好 `有栽过跟头 <http://coolshell.cn/articles/11112.html>`_ .
#. 其实我主张指针/地址操作符与变量名紧邻``int* a, b`` vs ``int *a, b``, 新手会误以为前者的 ``b````int *`` 变量,但后者就不一样了,高下立判。
#. 在这风格指南里我才刚知道 C++ 原来还有所谓的 `Alternative operator representations <http://en.cppreference.com/w/cpp/language/operator_alternative>`_, 大概没人用吧
#. 注意构造函数初始值列表Constructer Initializer List与列表初始化Initializer List是两码事,我就差点混淆了它们的翻译。
#. 事实上,如果您熟悉英语本身的书写规则,就会发现该风格指南在格式上的规定与英语语法相当一脉相承。比如普通标点符号和单词后面还有文本的话,总会留一个空格;特殊符号与单词之间就不用留了,比如 ``if (true)`` 中的圆括号与 ``true``.
#. 本风格指南没有明确规定 void 函数里要不要用 return 语句,不过就 Google 开源项目 leveldb 并没有写;此外从 `Is a blank return statement at the end of a function whos return type is void necessary? <http://stackoverflow.com/questions/9316717/is-a-blank-return-statement-at-the-end-of-a-function-whos-return-type-is-void-ne>`_ 来看``return;````return ;`` 更约定俗成(事实上 cpplint 会对后者报错,指出分号前有多余的空格),且可用来提前跳出函数栈。
#. 80 行限制事实上有助于避免代码可读性失控, 比如超多重嵌套块, 超多重函数调用等等.
#. Linux 上设置好了 Locale 就几乎一劳永逸设置好所有开发环境的编码, 不像奇葩的 Windows.
#. Google 强调有一对 if-else 时, 不论有没有嵌套, 都要有大括号. Apple 正好 `有栽过跟头 <http://coolshell.cn/articles/11112.html>`_ .
#. 其实我主张指针/地址操作符与变量名紧邻, ``int* a, b`` vs ``int *a, b``, 新手会误以为前者的 ``b````int *`` 变量, 但后者就不一样了, 高下立判.
#. 在这风格指南里我才刚知道 C++ 原来还有所谓的 `Alternative operator representations <http://en.cppreference.com/w/cpp/language/operator_alternative>`_, 大概没人用吧.
#. 注意构造函数初始值列表Constructer Initializer List与列表初始化Initializer List是两码事, 我就差点混淆了它们的翻译.
#. 事实上, 如果您熟悉英语本身的书写规则, 就会发现该风格指南在格式上的规定与英语语法相当一脉相承. 比如普通标点符号和单词后面还有文本的话, 总会留一个空格; 特殊符号与单词之间就不用留了, 比如 ``if (true)`` 中的圆括号与 ``true``.
#. 本风格指南没有明确规定 void 函数里要不要用 return 语句, 不过就 Google 开源项目 leveldb 并没有写; 此外从 `Is a blank return statement at the end of a function whos return type is void necessary? <http://stackoverflow.com/questions/9316717/is-a-blank-return-statement-at-the-end-of-a-function-whos-return-type-is-void-ne>`_ 来看, ``return;````return ;`` 更约定俗成(事实上 cpplint 会对后者报错, 指出分号前有多余的空格), 且可用来提前跳出函数栈.