http://herbsutter.com/gotw/
http://www.gotw.ca/gotw/
## 1 Variable Initialization – or Is It?
* Prefer to use initialization with `{ }`, such as `vector v = { 1, 2, 3, 4 };` or `auto v = vector{ 1, 2, 3, 4 };`, because it’s more consistent, more correct, and avoids having to know about old-style pitfalls at all. In single-argument cases where you prefer to see only the `=` sign, such as `int i = 42;` and `auto x = anything;` omitting the braces is fine. In rare cases, such as `vector v(10,20);` or `auto v = vector(10,20);`, use initialization with `( )` to explicitly call a constructor that is otherwise hidden by an `initializer_list` constructor.
最好使用`{ }`初始化,如`vector v = { 1, 2, 3, 4 };`或`auto v = vector{ 1, 2, 3, 4 };`,因为它更一致,正确,也不需要去了解使用旧方式的陷阱。在单参数的情况下,你只需要`=`符号而省略括号,如`int i = 42;`和`auto x = anything;`。极少数情况下,如`vector v(10,20);`或`auto v = vector(10,20);`,使用`( )`初始化是为了显式地调用非`initializer_list`构造函数。
* When you design a class, avoid providing a constructor that ambiguously overloads with an `initializer_list` constructor, so that users won’t need to use `( )` to reach such a hidden constructor.
当你设计一个类,避免提供一个能和`initializer_list`构造函数造成歧义重载的构造函数,这样用户就不会非得使用`( )`来调用它。
**Common Mistake**: `widget w = x;` This is always initialization; it is never assignment, and so it never calls `T::operator=()`. Yes, I know there’s an `=` character in there, but don’t let that throw you — that’s just a syntax holdover from C, not an assignment operation.
**常见错误**:`widget w = x;`这总是初始化,永远不会是赋值,不会调用`T::operator=()`。是的,我知道那有个`=`字符,但不要被迷惑了,那只是从C继承过来的语法而不是赋值操作。
## 2 Temporary Objects
* Prefer passing a read-only parameter by `const&` if you are only going to read from it (not make a copy of it).
优先使用`const&`传入一个只读参数,如果你只是打算读取它(而不是拷贝一份)。
* Prefer declaring variables using `auto`. Among other reasons to do so, it naturally guarantees zero extra temporaries due to implicit conversions.
优先使用`auto`声明变量。众多原因的一个:它自然地保证不会有由于隐式转换而产生的临时变量。
* Prefer precomputing values that won't change, instead of recreating objects unnecessarily.
优先预先计算好不会改变的值,避免不必要地重复创建对象。
* Write for clarity and correctness first. Don't optimize prematurely, before you have profiler data proving the optimization is needed, especially in the case of calls to simple inline calls to short functions that compilers normally can handle for you.
首先写代码要清晰和正确。在你做了数据测试证明需要优化之前,不要做不成熟的优化,特别有个例子是:把简短的函数调用变成内联的,编译器正常情况下会帮你完成。
* For consistency, always implement postincrement in terms of preincrement, otherwise your users will get surprising (and often unpleasant) results.
为了一致性,总是使用前增操作来实现后增操作,否则你的用户将会得到惊讶(和不满)的结果。
* Prefer preincrement. Only use postincrement if you're going to use the original value.
优先使用前增操作。只有在你需要使用原来的值的时候才使用后增操作。
* Watch out for hidden temporaries created by implicit conversions. One good way to avoid this is to make constructors and conversion operators `explicit` by default unless implicit conversions are really desirable.
小心隐式转换时产生的隐藏的临时变量。一个避免这种情况的好方法是使构造函数和转换操作函数默认为`explicit`,除非隐式转换是真正需要的。
**Premature optimization** is when you make code more complex in the name of efficiency without data that it's actually needed.
**不成熟的优化**是指你效率为由把代码变得更加复杂,而不考虑真正需要的数据。
**Premature pessimization** is when you write code that is slower than it needs to be, usually by asking for unnecessary extra work, when equivalently complex code would be faster and should just naturally flow out of your fingers.
**不成熟的劣化**是指你通过不必要的额外工作把代码写得比通常需要的更慢,而与之等价的稍显复杂的代码反而可以更快而且应该可以自然而然地写出来。
## 3 Using the Standard Library
* Prefer algorithm calls to explicit loops. Algorithm calls are often clearer and reduce complexity. If no suitable algorithm exists, why not write it? You’ll use it again.
优先使用算法而不是显式的循环。算法通常更清晰,减少复杂度。如果没有合适的算法存在,为什么不写一个呢?你会再次用到它的。
* Reuse code—especially standard library code—instead of handcrafting your own. It’s faster, easier, and safer.
重用代码 - 特别是标准库代码来代替你自己手写的代码。它更快,更容易,更安全。
## 4 Class Mechanics
* If you supply a standalone version of an operator (e.g., `operator+`), always supply an assignment version of the same operator (e.g., `operator+=`) and prefer implementing the former in terms of the latter. Also, always preserve the natural relationship between op and op= (where op stands for any operator).
如果你提供了一个单独版本的操作符函数(如:`operator+`),总是还应该提供它的一个赋值版本(如:`operator+=`),而且最好用后一个版本来实现前一个版本。此外,总是应该保持op和op=(op为任意操作符)之间的自然关系。
* Prefer writing `a op= b` instead of `a = a op b` (where op stands for any operator). It’s clearer, and it’s often more efficient.
优先使用`a op= b`来代替`a = a op b`。它更清晰,通常也更具有效率。
* Prefer passing a read-only parameter by value if you’re going to make a copy of the parameter anyway, because it enables move from rvalue arguments.
如果你打算复制一份参数的拷贝,优先使用传值的形式来传入只读参数,因为这样可以移动右值实参。
* Prefer these guidelines for making an operator a member vs. nonmember function: unary operators are members; `= () [] ->` must be members; the assignment operators (`+= –= /= *=` etc.) must be members; all other binary operators are nonmembers.
使用下列规则来决定一个操作符函数是类成员函数还是非类成员函数:一元操作符是成员函数;`= () [] ->`是成员函数;赋值操作符(`+= –= /= *=` 等) 是成员函数;剩下其它二元操作符(`+ - / * << >>`等)是非成员函数。
* Always return stream references from `operator<<` and `operator>>`.
`operator<<`和`operator>>`总是应返回stream的引用。
* When you `return *this`, the return type should usually be a reference.
当你`return *this`,返回类型应该总是为一个引用。
## 5 Overriding Virtual Functions
* Don't use explicit `new`, `delete`, and owning `*` pointers, except in rare cases encapsulated inside the implementation of a low-level data structure.
不要显式地使用`new`, `delete`,及含有`*`指针,除非在极少数情况下被封装在底层数据结构的实现中。
* Make base class destructors public and virtual, or protected and nonvirtual.
使基类的析构函数公有且虚的,或者保护且非虚的。
* When providing a non-overridden function with the same name as an inherited function, be sure to bring the inherited functions into scope with a using-declaration if you don’t want to hide them.
当提供一个非多态函数及相同名字的继承类里的函数,如果你不想隐藏基类的函数(而是要重载它)就要保证用using声明把基类的函数放到和派生类相同的作用域中。
* Always write `override` when you intend to override a virtual function.
当你要重写一个(多态)虚函数时,总是写上`override`。
* Never change the default arguments of overridden inherited functions.
从不改变多态继承而来函数的默认参数的值。
* Avoid default arguments on virtual functions in general.
通常应避免在虚函数中使用默认参数。
* Prefer to have a class contain only public virtual functions, or no public virtual functions (other than the destructor which is special). A pure abstract base class should have only public virtual functions. For any other base class, prefer making public member functions non-virtual, and virtual member functions non-public; the former should have any default arguments and can be implemented in terms of the latter.
在一个类中只有公有虚函数或非公有虚函数(析构函数除外)。一个纯抽象基类应只有公有虚函数。对于其它的基类,最好使公有函数非虚的,使虚函数非公有;前者可以有默认参数,且可以由后者来实现(NVI,template模式)。
## 6 Const-Correctness
* Remember the “M&M rule”: For a member variable, `mutable` and `mutex` (or `atomic`) go together.
记住“M&M规则”:对于成员变量,`mutable`和`mutex`(或`atomic`)必须一起使用。
1. For a member variable, **mutable implies mutex (or equivalent)**: A `mutable` member variable is presumed to be a mutable shared variable and so must be synchronized internally—protected with a `mutex`, made `atomic`, or similar.
对于成员变量,**mutable意味着mutex(或等价物)**:一个`mutable`的成员变量是被假设为可变的共享变量,必须是内部同步的 - 通过`mutex`保护,变成`atomic`或类似的机制。
1. For a member variable, **mutex (or similar synchronization type) implies mutable**: A member variable that is itself of a synchronization type, such as a `mutex` or a condition variable, naturally wants to be `mutable`, because you will want to use it in a `non-const` way (e.g., take a `std::lock_guard`) inside concurrent `const` member functions.
对于成员变量,**mutex(或类似同步类型)意味着mutable**:一个同步类型的成员变量,如`mutex`或条件变量,自然要求是`mutable`的,因为你想用`non-const`的方式(如:使用一个`std::lock_guard`)在`const`成员函数中并发地使用它。
* Consider not writing `const` on pass-by-value function parameters when only forward-declaring a function. You can always add it on the definition to express a read-only parameter.
不要考虑把`const`用在函数声明的传值参数上(加不加一样,不是函数签名的一部分)。你可以只把它加在函数定义处来表达这是一个只读的参数。
* Prefer range-based `for` loops to naked iterator-incrementing `for` loops when visiting the elements of the range in order.
当顺序访问范围内元素时,优先使用基于范围的`for`循环而不是迭代递增的`for`循环。
* Prefer to make variables, including locals, `const` if they should not change.
如果一个变量(包括局部变量)不变,最好把它声明为`const`的。
## 7 Minimizing Compile-Time Dependencies
* Never `#include` unnecessary header files.
从不`#include`不必要的头文件。
* Prefer to `#include ` when a forward declaration of a stream will suffice.
如果只需要一个stream的前向声明,优先包含`#include `。
* Never `#include` a header when a forward declaration will suffice.
如果一个前向声明可以满足的话就不要`#include`头文件。
* For widely-included classes whose implementations may change, or to provide ABI-safety or binary compatibility, consider using the compiler-firewall idiom (Pimpl Idiom) to hide implementation details. Use an opaque pointer (a pointer to a declared but undefined class) declared as `struct impl; std::unique_ptr pimpl;` to store private nonvirtual members.
对于会被广泛包含而实现又经常改变的类,或为了提供ABI安全性或二进制兼容性,考虑使用编译期防火墙手法(Pimpl)来隐藏实现细节。使用一个不透明指针(一个声明了但未定义的类的指针)并声明为`struct impl; std::unique_ptr pimpl;`来存放私有非虚成员。
* Never inherit when composition is sufficient.
如果组合能满足就不要用继承。
## 89 Smart Pointers
* Prefer to use the standard smart pointers, `unique_ptr` by default and `shared_ptr` if sharing is needed. They are the common types that all C++ libraries can understand. Use other smart pointer types only if necessary for interoperability with other libraries, or when necessary for custom behavior you can’t achieve with deleters and allocators on the standard pointers.
优先使用标准的智能指针,默认使用`unique_ptr`以及如果需要共享的话`shared_ptr`。它们是所有C++库都能理解的普通类型。仅在需要和其它库互操作或需要通过标准智能指针的删除器和分配器所不能达到的自定义的行为的时候使用其它的智能指针类型。
* Use `make_shared` (or, if you need a custom allocator, `allocate_shared`) to create an object you know will be owned by `shared_ptr`s, unless you need a custom deleter or are adopting a raw pointer from elsewhere.
使用`make_shared`(如果需要自定义的分配器,使用`allocate_shared`)来创建一个将被`shared_ptr`接管的对象,除非你需要自定义的删除器或从别处接管一个裸指针。
* Use `make_unique` to create an object that isn’t shared (at least not yet), unless you need a custom deleter or are adopting a raw pointer from elsewhere.
使用`make_unique`来创建一个不被共享(至少现在还没有)的对象,除非你需要自定义的删除器或从别处接管一个裸指针。
* To allocate an object, prefer to write `make_unique` by default, and write `make_shared` when you know the object’s lifetime is going to be managed by using `shared_ptr`s.
为了分配空间给一个对象,优先默认使用`make_unique`,当你知道这个对象的生命周期将会被`shared_ptr`管理的时候使用`make_shared`。
## 90 Factories
* A factory that produces a reference type should return a `unique_ptr` by default, or a `shared_ptr` if ownership is to be shared with the factory.
一个产生引用类型的工厂应该默认返回一个`unique_ptr`,如果所有权和工厂共享的话则返回一个`shared_ptr`。
* Prefer declaring variables using `auto`. It’s shorter, and helps to insulate your code from needless source ripples due to minor type changes.
优先使用`auto`声明变量。它更短,帮助把你的代码从由于类型小改动而导致的无需修改的源代码中隔离出来。
* A factory that produces a non-reference type should return a value by default, and throw an exception if it fails to create the object. If not creating the object can be a normal result, return an `optional<>` value.
一个产生非引用类型的工厂应该默认返回一个值类型,如果创建失败则抛出一个异常。如果不能创建对象是个正常的结果,返回一个`optional<>`类型的值。
## 91 Smart Pointer Parameters
* Don't pass a smart pointer as a function parameter unless you want to use or manipulate the smart pointer itself, such as to share or transfer ownership. Prefer passing objects by value, `*`, or `&`, not by smart pointer.
不要传一个智能指针作为函数的参数除非你想使用或操作其本身,比如共享或转移所有权。最好通过值,`*`或`&`而不是智能指针传递对象。
* Express a "sink" function using a by-value `unique_ptr` parameter.
用传值的`unique_ptr`参数来表示这是一个“凹槽”(所有权转移)函数。
* Use a non-const `unique_ptr&` parameter only to modify the `unique_ptr`.
仅在需要修改`unique_ptr`本身(指向另一个对象)的情况下使用一个非const的`unique_ptr&`参数。
* Don't use a `const unique_ptr&` as a parameter; use `widget*` instead.
不要使用`const unique_ptr&`作为参数,用`widget*`代替。
* Express that a function will store and share ownership of a heap object using a by-value `shared_ptr` parameter.
使用一个传值的`shared_ptr`参数来表示一个函数将会保存及共享堆上对象的所有权(参数已拷贝,再move到保存的地方)。
* Use a non-const `shared_ptr&` parameter only to modify the `shared_ptr`. Use a const `shared_ptr&` as a parameter only if you’re not sure whether or not you’ll take a copy and share ownership; otherwise use `widget*` instead (or if not nullable, a `widget&`).
仅在修改`shared_ptr`本身时使用非const`shared_ptr&`参数。仅在你不确定是否会复制和共享所有权的时候使用`const shared_ptr&`参数(需要时再进行拷贝),否则使用`widget*`代替(如果不为空的话,使用`widget&`)。
## 92 Auto Variables, Part 1
## 93 Auto Variables, Part 2
* Prefer to declare local variables using `auto x = expr;` when you don’t need to explicitly commit to a type. It is simpler, guarantees that you will use the correct type, and guarantees that the type stays correct under maintenance.
如果你不需要显式地表明一个类型,最好使用`auto x = expr;`声明局部变量。它更简单,保证你使用正确的类型,保证代码维护的时候类型仍保持正确。
* Prefer to declare local variables using `auto x = expr;` when you don’t need to explicitly commit to a type. It is efficient by default and guarantees that no implicit conversions or temporary objects will occur.
如果你不需要显式地表明一个类型,最好使用`auto x = expr;`声明局部变量。它默认更有效,保证没有隐式转换和临时对象的产生。
* Prefer to declare local variables `auto x = type{ expr };` when you do want to explicitly commit to a type. It is self-documenting to show that the code is explicitly requesting a conversion.
如果你需要显式地表明一个类型,最好使用`auto x = type{ expr };`声明局部变量。它自己就表明了这段代码是显式地需要类型转换。
* Prefer to use `auto name =` to name a lambda function object. Use `std::function*…*/> name =` only when you need to rebind it to another target or pass it to another function that needs a `std::function<>`.
优先使用`auto name =`来保存一个匿名函数对象。仅在你需要把它重新绑定到另一个目标上或传到另一个需要`std::function<>`类型的函数中时使用`std::function*…*/> name =`。
* Prefer to use `auto` lambda parameter types. They are just as efficient as explicit parameter types, and allow you to call the same lambda with different argument types.
优先使用`auto`作为匿名函数的参数类型。它们和显式参数类型同样有效率,可以让你以不同的参数类型来调用相同的匿名函数。
* Prefer to use implicit return type deduction for lambda functions.
对于匿名函数,最好使用隐式地推导出返回类型。
* Always initialize variables, except only when you can prove garbage values are okay, typically because you will immediately overwrite the contents.
总是初始化变量,除非你能证明垃圾数据是没问题的,常是因为你将立即覆盖变量内容。
* Prefer to declare local variables using `auto`. It guarantees that you cannot accidentally leave the variable uninitialized.
最好使用`auto`声明局部变量。它保证你不会意外地未初始化一个变量。
* Prefer to declare local variables using `auto`. It guarantees that you get the exact type and cannot accidentally get narrowing conversions.
最好使用`auto`声明局部变量。它保证你能得到精确的类型,不会意外地发生丢失精度的转换。
* Prefer to declare local variables using `auto`. It guarantees that you get the exact type and so is the simplest way to portably spell the implementation-specific type of arithmetic operations on built-in types, which vary by platform, and ensure that you cannot accidentally get narrowing conversions when storing the result.
最好使用`auto`声明局部变量。它保证你能得到精确的类型,这是最简单的方法来可移植地声明实现相关的基于内置类型算术操作得到的类型,这些类型不同的平台可能都不一样,而且保证你在保存结果的时候不会意外地发生丢失精度的转换。
* Prefer using the `` type aliases in code that cares about the size of your numeric variables. Avoid relying on what your current platform(s) happen to do.
最好在关心数值变量大小的代码里使用``里面的类型别名。避免依赖你当前平台的实现。
* Prefer to declare local variables `auto x = type{ expr };` when you do want to explicitly commit to a type. It is self-documenting to show that the code is explicitly requesting a conversion, and won’t allow an accidental implicit narrowing conversion. Only when you do want explicit narrowing, use `( )` instead of `{ }`.
如果你需要显式地表明一个类型,最好使用`auto x = type{ expr };`声明局部变量。它自己就表明了这段代码是显式地需要类型转换,而且不允许意外地进行丢失精度的转换。如果你想显式地丢掉进度,使用`( )`来代替`{ }`。
* Combine signed and unsigned arithmetic carefully.
进行有符号和无符号混合算术运算时要小心。
* Prefer to use `auto x = as_signed(integer_expr);` or `auto x = as_unsigned(integer_expr);` to store the result of an integer computation that should be signed or unsigned. Using `auto` together with `as_signed` or `as_unsigned` guarantees portable code: that the variable will both be large enough and preserve the required signedness on all platforms.
最好使用`auto x = as_signed(integer_expr);` 或`auto x = as_unsigned(integer_expr);`来保存整形计算出来的有符号或无符号的结果。把`auto`和`as_signed`或`as_unsigned`一起使用可以保证可移植的代码:变量在所有平台上将会足够大及保持所需要的符号性。
## 94 AAA Style (Almost Always Auto)
* Remember that preferring `auto` variables is motivated primarily by correctness, performance, maintainability, and robustness—and only lastly about typing convenience.
记住优先使用`auto`主要是为了正确性,性能,可维护性以及强壮性,最后简化书写。
## 95 Thread Safety and Synchronization
* Reads (`const` operations) on a shared object are safe to run concurrently with each other without synchronization.
在一个共享对象上的读操作(`const`操作)可以安全地并发进行而不需要同步。
* A type should only be fully internally synchronized if and only if its purpose is to provide inter-thread communication (e.g., a message queue) or synchronization (e.g., a mutex).
如果一个类型的目的只是提供线程间通信(如:一个消息队列)或同步(如:一个互斥体),它应该是完全内部同步的。
* The code that knows about and owns a writeable shared variable is responsible for synchronizing access to it.
知道和拥有(可能被不同线程)可写共享变量的代码有责任同步地访问它。
* It is always true that the code that knows about and owns a writeable shared variable is responsible for synchronizing access to it. If the writeable shared state is hidden inside the implementation of some class, then it’s simply that class’ internals that are the ‘owning code’ that has to synchronize access to (just) the shared state that only it knows about.
上一条总为真。如果可写的共享状态被一些类隐藏在实现内部,那么只有这些类的内部代码拥有并知道它的共享状态从而必须同步地访问它。
* If you design a class where two objects may invisibly share state under the covers, it is your class’ responsibility to internally synchronize access to that `mutable` shared state (only) that it owns and that only it can see, because the calling code can’t. If you opt for under-the-covers-sharing strategies like copy-on-write, be aware of the duty you’re taking on for yourself and code with care.
如果你设计一个类,它的两个对象可能在内部共享状态,你的类有责任在内部同步地访问它所拥有也只有它能看见的`mutable`共享变量,因为调用代码无法做到。如果你采用了内部共享技术如写时复制,了解你自己(设计的类)的责任,小心编码。