赞
踩
英文原版:https://google.github.io/styleguide/cppguide.html
GitHub仓库:https://github.com/google/styleguide
中文翻译:https://zh-google-styleguide.readthedocs.io/en/latest/
GitHub仓库:https://github.com/zh-google-styleguide/zh-google-styleguide
以下是从中文翻译版中截取了一部分常用内容,并附上了原文链接:
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/magic/#section-1
动态分配出的对象最好有单一且固定的所有主, 并通过智能指针传递所有权.
如果必须使用动态分配, 那么更倾向于将所有权保持在分配者手中. 如果其他地方要使用这个对象, 最好传递它的拷贝, 或者传递一个不用改变所有权的指针或引用. 倾向于使用 std::unique_ptr
来明确所有权传递, 例如:
std::unique_ptr<Foo> FooFactory();
void FooConsumer(std::unique_ptr<Foo> ptr);
如果没有很好的理由, 则不要使用共享所有权. 这里的理由可以是为了避免开销昂贵的拷贝操作, 但是只有当性能提升非常明显, 并且操作的对象是不可变的(比如说 std::shared_ptr<const Foo>
)时候, 才能这么做. 如果确实要使用共享所有权, 建议于使用 std::shared_ptr
.
不要使用 std::auto_ptr
, 使用 std::unique_ptr
代替它.
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/magic/#cpplint
使用 cpplint.py
检查风格错误.
cpplint.py
是一个用来分析源文件, 能检查出多种风格错误的工具. 它不并完美, 甚至还会漏报和误报, 但它仍然是一个非常有用的工具. 在行尾加 // NOLINT
, 或在上一行加 // NOLINTNEXTLINE
, 可以忽略报错.
某些项目会指导你如何使用他们的项目工具运行 cpplint.py
. 如果你参与的项目没有提供, 你可以单独下载 cpplint.py.
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#section-2
用于定义移动构造函数 (使用类的右值引用进行构造的函数) 使得移动一个值而非拷贝之成为可能. 例如, 如果 v1
是一个 vector<string>
, 则 auto v2(std::move(v1))
将很可能不再进行大量的数据复制而只是简单地进行指针操作, 在某些情况下这将带来大幅度的性能提升.
std::forward
功能函数. 你可能会使用
std::move
来表示将值从一个对象移动而不是复制到另一个对象.
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#alloca
不允许使用变长数组和 alloca()
.
变长数组和 alloca()
不是标准 C++ 的组成部分. 更重要的是, 它们根据数据大小动态分配堆栈内存, 会引起难以发现的内存越界 bugs: “在我的机器上运行的好好的, 发布后却莫名其妙的挂掉了”.
std::vector
或
std::unique_ptr<T[]>
.
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#section-6
Google 现有的大多数 C++ 代码都没有异常处理。Google 禁止使用异常这一点, 仅仅是为了自身的方便, 说大了, 无非是基于软件管理成本上, 实际使用中还是自己决定。
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#section-7
禁止使用 RTTI.
RTTI 有合理的用途但是容易被滥用, 因此在使用时请务必注意. 在单元测试中可以使用 RTTI, 但是在其他代码中请尽量避免. 尤其是在新代码中, 使用 RTTI 前务必三思. 如果你的代码需要根据不同的对象类型执行不同的行为的话, 请考虑用以下的两种替代方案之一查询类型:
虚函数可以根据子类类型的不同而执行不同代码. 这是把工作交给了对象本身去处理.
如果这一工作需要在对象之外完成, 可以考虑使用双重分发的方案, 例如使用访问者设计模式. 这就能够在对象之外进行类型判断.
如果程序能够保证给定的基类实例实际上都是某个派生类的实例, 那么就可以自由使用 dynamic_cast. 在这种情况下, 使用 dynamic_cast 也是一种替代方案.
基于类型的判断树是一个很强的暗示, 它说明你的代码已经偏离正轨了. 不要像下面这样:
if (typeid(*data) == typeid(D1)) {
...
} else if (typeid(*data) == typeid(D2)) {
...
} else if (typeid(*data) == typeid(D3)) {
...
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#section-8
使用 C++ 的类型转换, 如 static_cast<>()
. 不要使用 int y = (int)x
或 int y = int(x)
等转换方式;
不要使用 C 风格类型转换. 而应该使用 C++ 风格.
- 用
static_cast
替代 C 风格的值转换, 或某个类指针需要明确的向上转换为父类指针时.- 用
const_cast
去掉const
限定符.- 用
reinterpret_cast
指针类型和整型或其它指针之间进行不安全的相互转换. 仅在你对所做一切了然于心时使用.
至于 dynamic_cast
参见 6.8. 运行时类型识别.
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#section-9
只在记录日志时使用流.
pread()
等功能函数很难执行. 如果不使用
printf
风格的格式化字符串, 某些格式化操作 (尤其是常用的格式字符串
%.*s
) 用流处理性能是很低的. 流不支持字符串操作符重新排序 (%1s), 而这一点对于软件国际化很有用.
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#section-10
对于迭代器和其他模板对象使用前缀形式 (++i
) 的自增, 自减运算符.
++i
) 通常要比后置自增 (
i++
) 效率更高. 因为后置自增 (或自减) 需要对表达式的值
i
进行一次拷贝. 如果
i
是迭代器或其他非数值类型, 拷贝的代价是比较大的. 既然两种自增方式实现的功能一样, 为什么不总是使用前置自增呢?
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#const
在声明的变量或参数前加上关键字 const
用于指明变量值不可被篡改 (如 const int foo
). 为类中的函数加上 const
限定符表明该函数不会修改类成员变量的状态 (如 class Foo { int Bar(char c) const; };
).
const
变量, 数据成员, 函数和参数为编译时类型检测增加了一层保障; 便于尽早发现错误. 因此, 我们强烈建议在任何可能的情况下使用 const
:
const
.const
. 访问函数应该总是 const
. 其他不会修改任何数据成员, 未调用非 const
函数, 不会返回数据成员非 const
指针或引用的函数也应该声明成 const
.const
.然而, 也不要发了疯似的使用 const
. 像 const int * const * const x;
就有些过了, 虽然它非常精确的描述了常量 x
. 关注真正有帮助意义的信息: 前面的例子写成 const int** x
就够了.
关键字 mutable
可以使用, 但是在多线程中是不安全的, 使用时首先要考虑线程安全.
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#constexpr
在 C++11 里,用 constexpr
来定义真正的常量,或实现常量初始化。
变量可以被声明成 constexpr
以表示它是真正意义上的常量,即在编译时和运行时都不变。函数或构造函数也可以被声明成 constexpr
, 以用来定义 constexpr
变量。
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#section-11
C++ 内建整型中, 仅使用 int
. 如果程序中需要不同大小的变量, 可以使用 <stdint.h>
中长度精确的整型, 如 int16_t
.如果您的变量可能不小于 2^31 (2GiB), 就用 64 位变量比如 int64_t
. 此外要留意,哪怕您的值并不会超出 int 所能够表示的范围,在计算过程中也可能会溢出。所以拿不准时,干脆用更大的类型。
C++ 没有指定整型的大小. 通常人们假定 short
是 16 位, int
是 32 位, long
是 32 位, long long
是 64 位.
<stdint.h>
定义了 int16_t
, uint32_t
, int64_t
等整型, 在需要确保整型大小时可以使用它们代替 short
, unsigned long long
等. 在 C 整型中, 只使用 int
. 在合适的情况下, 推荐使用标准类型如 size_t
和 ptrdiff_t
.
如果已知整数不会太大, 我们常常会使用 int
, 如循环计数. 在类似的情况下使用原生类型 int
. 你可以认为 int
至少为 32 位, 但不要认为它会多于 32
位. 如果需要 64 位整型, 用 int64_t
或 uint64_t
.
对于大整数, 使用 int64_t
.
不要使用 uint32_t
等无符号整型, 除非你是在表示一个位组而不是一个数值, 或是你需要定义二进制补码溢出. 尤其是不要为了指出数值永不会为负, 而使用无符号类型. 相反, 你应该使用断言来保护数据.
如果您的代码涉及容器返回的大小(size),确保其类型足以应付容器各种可能的用法。拿不准时,类型越大越好。
小心整型类型转换和整型提升(acgtyrant 注:integer promotions, 比如 int
与 unsigned int
运算时,前者被提升为 unsigned int
而有可能溢出),总有意想不到的后果。
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#section-13
使用宏时要非常谨慎, 尽量以内联函数, 枚举和常量代替之.
下面给出的用法模式可以避免使用宏带来的问题; 如果你要宏, 尽可能遵守:
.h
文件中定义宏.#define
, 使用后要立即 #undef
.##
处理函数,类和变量的名字。https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#nullptr-null
整数用 0
, 实数用 0.0
, 指针用 nullptr
或 NULL
, 字符 (串) 用 '\0'
.
对于指针 (地址值), 到底是用 0
, NULL
还是 nullptr
. C++11 项目用 nullptr
; C++03 项目则用 NULL
, 毕竟它看起来像指针。实际上,一些 C++ 编译器对 NULL
的定义比较特殊,可以输出有用的警告,特别是 sizeof(NULL)
就和 sizeof(0)
不一样。
字符 (串) 用 '\0'
, 不仅类型正确而且可读性好.
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#sizeof
尽可能用 sizeof(varname)
代替 sizeof(type)
.
使用 sizeof(varname)
是因为当代码中变量类型改变时会自动更新. 您或许会用 sizeof(type)
处理不涉及任何变量的代码,比如处理来自外部或内部的数据格式,这时用变量就不合适了。
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#auto
用 auto
绕过烦琐的类型名,只要可读性好就继续用,别用在局部变量之外的地方。
auto
只能用在局部变量里用。别用在文件作用域变量,命名空间作用域变量和类数据成员里。永远别列表初始化 auto
变量。
auto 和 C++11 列表初始化的合体令人摸不着头脑:
auto x(3); // 圆括号。
auto y{3}; // 大括号。
它们不是同一回事——x
是 int
, y
则是 std::initializer_list<int>
. 其它一般不可见的代理类型(acgtyrant 注:normally-invisible proxy types, 它涉及到 C++ 鲜为人知的坑:Why is vector<bool> not a STL container?)也有大同小异的陷阱。
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#section-14
用户自定义类型也可以定义接收 std::initializer_list<T>
的构造函数和赋值运算符,以自动列表初始化:
class MyType {
public:
// std::initializer_list 专门接收 init 列表。
// 得以值传递。
MyType(std::initializer_list<int> init_list) {
for (int i : init_list) append(i);
}
MyType& operator=(std::initializer_list<int> init_list) {
clear();
for (int i : init_list) append(i);
}
};
MyType m{2, 3, 5, 7};
列表初始化也适用于常规数据类型的构造,哪怕没有接收 std::initializer_list<T>
的构造函数。
double d{1.23};
// MyOtherType 没有 std::initializer_list 构造函数,
// 直接上接收常规类型的构造函数。
class MyOtherType {
public:
explicit MyOtherType(string);
MyOtherType(int, string);
};
MyOtherType m = {1, "b"};
// 不过如果构造函数是显式的(explict),您就不能用 `= {}` 了。
MyOtherType m{"b"};
千万别直接列表初始化 auto 变量,看下一句,估计没人看得懂:
auto d = {1.23}; // d 即是 std::initializer_list<double>
auto d = double{1.23}; // 善哉 -- d 即为 double, 并非 std::initializer_list.
至于格式化,参见9.7. 列表初始化格式
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#lambda
适当使用 lambda 表达式。别用默认 lambda 捕获,所有捕获都要显式写出来。
[=](int x) {return x + n;}
, 您该写成 [n](int x) {return x + n;}
才对,这样读者也好一眼看出 n
是被捕获的值。https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#section-15
不要使用复杂的模板编程
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/others/#boost-1
只使用 Boost 中被认可的库.
Boost 库集 是一个广受欢迎, 经过同行鉴定, 免费开源的 C++ 库集.
为了向阅读和维护代码的人员提供更好的可读性, 我们只允许使用 Boost 一部分经认可的特性子集. 目前允许使用以下库:
boost/call_traits.hpp
boost/compressed_pair.hpp
boost/graph
, except serialization (adj_list_serialize.hpp
) and parallel/distributed algorithms and data structures(boost/graph/parallel/*
and boost/graph/distributed/*
)boost/property_map.hpp
boost/iterator/iterator_adaptor.hpp
, boost/iterator/iterator_facade.hpp
, and boost/function_output_iterator.hpp
boost/polygon/voronoi_builder.hpp
, boost/polygon/voronoi_diagram.hpp
, and boost/polygon/voronoi_geometry_type.hpp
boost/bimap
boost/math/distributions
boost/multi_index
boost/heap
boost/container/flat_map
, and boost/container/flat_set
我们正在积极考虑增加其它 Boost 特性, 所以列表中的规则将不断变化.
以下库可以用,但由于如今已经被 C++ 11 标准库取代,不再鼓励:
boost/ptr_container
, 改用 std::unique_ptrboost/array.hpp
, 改用 std::array前面说明的编程习惯基本都是强制性的. 但所有优秀的规则都允许例外, 这里就是探讨这些特例.
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/exceptions/#section-2
对于现有不符合既定编程风格的代码可以网开一面.
当你修改使用其他风格的代码时, 为了与代码原有风格保持一致可以不使用本指南约定. 如果不放心, 可以与代码原作者或现在的负责人员商讨. 记住, 一致性也包括原有的一致性.
https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/exceptions/#windows
总述
Windows 程序员有自己的编程习惯, 主要源于 Windows 头文件和其它 Microsoft 代码. 我们希望任何人都可以顺利读懂你的代码, 所以针对所有平台的 C++ 编程只给出一个单独的指南.
说明
如果你习惯使用 Windows 编码风格, 这儿有必要重申一下某些你可能会忘记的指南:
iNum
). 使用 Google 命名约定, 包括对源文件使用 .cc
扩展名.DWORD
, HANDLE
等等. 在调用 Windows API 时这是完全可以接受甚至鼓励的. 即使如此, 还是尽量使用原有的 C++ 类型, 例如使用 const TCHAR *
而不是 LPCTSTR
.#pragma once
; 而应该使用 Google 的头文件保护规则. 头文件保护的路径应该相对于项目根目录 (Yang.Y 注: 如 #ifndef SRC_DIR_BAR_H_
, 参考 #define 保护 一节).#pragma
和 __declspec
. 使用 __declspec(dllimport)
和 __declspec(dllexport)
是允许的, 但必须通过宏来使用, 比如 DLLIMPORT
和 DLLEXPORT
, 这样其他人在分享使用这些代码时可以很容易地禁用这些扩展.然而, 在 Windows 上仍然有一些我们偶尔需要违反的规则:
_ATL_NO_EXCEPTIONS
以禁用异常. 你需要研究一下是否能够禁用 STL 的异常, 如果无法禁用, 可以启用编译器异常. (注意这只是为了编译 STL, 自己的代码里仍然不应当包含异常处理).StdAfx.h
或 precompile.h
的文件. 为了使代码方便与其他项目共享, 请避免显式包含此文件 (除了在 precompile.cc
中), 使用 /FI
编译器选项以自动包含该文件.resource.h
且只包含宏, 这一文件不需要遵守本风格指南.https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/end/
运用常识和判断力, 并且 保持一致.
编辑代码时, 花点时间看看项目中的其它代码, 并熟悉其风格. 如果其它代码中 if
语句使用空格, 那么你也要使用. 如果其中的注释用星号 (*) 围成一个盒子状, 那么你同样要这么做.
风格指南的重点在于提供一个通用的编程规范, 这样大家可以把精力集中在实现内容而不是表现形式上. 我们展示的是一个总体的的风格规范, 但局部风格也很重要, 如果你在一个文件中新加的代码和原有代码风格相去甚远, 这就破坏了文件本身的整体美观, 也让打乱读者在阅读代码时的节奏, 所以要尽量避免.
好了, 关于编码风格写的够多了; 代码本身才更有趣. 尽情享受吧!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。