【C/C++】Google 出品的代码规范(Google C++ Style Guide)
文章目录
- I. 开篇, 一图胜千言
- II. 分门别类, 娓娓道来
- Principles 原则
- Header Files 头文件
- Self-contained Headers
- The #define Guard #define 保护
- Forward Declarations 前置声明
- Inline Functions 内联函数
- Names and Order of Includes
- Scope 域
- Namespaces 命名空间
- Nonmember, Static Member, and Global Functions
- Local Variables
- Static and Global Variables
- Classes 类
- Doing Work in Constructors
- Implicit Conversions
- Copyable and Movable Types
- Structs vs. Classes
- Inheritance
- Operator Overloading
- Access Control
- Declaration Order
- Functions 函数
- Output Parameters
- Write Short Functions
- Reference Arguments
- Function Overloading
- Default Arguments
- Trailing Return Type Syntax
- Google-Specific Magic
- Other C++ Features
- Rvalue References
- Friends
- Exceptions
- noexcept
- Run-Time Type Information(RTTI)
- Casting
- Streams
- Preincrement and Predecrement
- Use of const
- Use of constexpr
- Integer Types
- 64-bit Portabaility
- Preprocessor Macros
- 0 and nullptr/NULL
- sizeof
- auto
- Braced Initializer List
- Lambda Expressions
- Template metaprogramming
- Boost
- std::hash
- C++ 11
- Nonstandard Extensions
- Aliases
- Naming 命名
- 命名风格
- 一般规则
- File Names
- Type Names
- Variable Names
- Class Data Members
- Struct Data Members
- Function Names
- Namespace Names
- Macro Names
- Enumerator Names
- Exceptions to Naming Rules
- Comments 注释
- Formatting
- Line Length
- Non-ASCII Characters
- Spaces vs. Tabs
- Function Declarations and Definitions
- Lambda Expressions
- Function Calls
- Braced Initializer List Format
- Conditionals
- Loops and Switch Statements
- Pointer and Reference Expresions
- Boolean Expressions
- Return Values
- Variable and Array Initialization
- Preprocessor Directives
- Class Format
- Constructor Initializer Lists
- Namespace Formatting
- Horizontal Whitespace
- Vertical Whitespace
- Exceptions to the Rules
- Parting Words : Use common sense and BE CONSISTENT
- III. 专题深入, 积沙成塔
- Ref
I. 开篇, 一图胜千言
具体细节,娓娓道来。
II. 分门别类, 娓娓道来
本章来自于官方文档的摘录. Google C++ Style Guide
Principles 原则
- Style rules should pull their weight.
- Optimize for the reader, not the writer
- Be consistent with existing code
- Be consistent with the broader C++ community when appropriate
- Avoid surprising or dangerous constructs
- Avoid constructs that our average C++ programmer would find tricky or hard to maintain
- Be mindful of our scale
- Concede to optimization when necessary
Header Files 头文件
Self-contained Headers
Header files should be self-contained (compile on their own) and end in .h. Non-header files that are meant for inclusion should end in .inc and be used sparingly.
The #define Guard #define 保护
- 按文件夹顺序:
<PROJECT>_<PATH>_<FILE>_H_
.
. For example, the file foo/src/bar/baz.h
in project foo
should have the following guard:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
Forward Declarations 前置声明
A “forward declaration” is a declaration of a class, function, or template without an associated definition.
- 尽量不用, 需要什么就
#include
Inline Functions 内联函数
- 小于 10 行的简单直接的小程序, 才写成内联函数
Names and Order of Includes
In dir/foo.cc or dir/foo_test.cc, whose main purpose is to implement or test the stuff in dir2/foo2.h, order your includes as follows:
- dir2/foo2.h.
- A blank line
- C system files.
- C++ system files.
- A blank line
- Other libraries’ .h files.
- Your project’s .h files.
For example, the includes in google-awesome-project/src/foo/internal/fooserver.cc
might look like this:
#include "foo/server/fooserver.h"
#include <sys/types.h>
#include <unistd.h>
#include <vector>
#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/server/bar.h"
- system-specific code needs conditional includes. 后置
#include "foo/public/fooserver.h"
#include "base/port.h" // For LANG_CXX11.
#ifdef LANG_CXX11
#include <initializer_list>
#endif // LANG_CXX11
Scope 域
Namespaces 命名空间
- 所有逻辑代码一律放在 namespace 里
// In the .h file
namespace mynamespace {
// All declarations are within the namespace scope.
// Notice the lack of indentation.
class MyClass {
public:
...
void Foo();
};
} // namespace mynamespace
// In the .cc file
namespace mynamespace {
// Definition of functions is within scope of the namespace.
void MyClass::Foo() {
...
}
} // namespace mynamespace
- 一律禁止使用
using namespace XXX
, 如using namespace std
- 可以使用
using
, 如using std::string
- 不使用
inline namespace
.
Nonmember, Static Member, and Global Functions
- 放在单独的 namespace 中
Local Variables
- Place a function’s variables in the narrowest scope possible, and initialize variables in the declaration.
int i;
i = f(); // Bad -- initialization separate from declaration.
int j = g(); // Good -- declaration has initialization.
std::vector<int> v;
v.push_back(1); // Prefer initializing using brace initialization.
v.push_back(2);
std::vector<int> v = {1, 2}; // Good -- v starts initialized.
//Variables needed for if, while and for statements should normally be declared within those statements,
// so that such variables are confined to those scopes. E.g.:
while (const char* p = strchr(str, '/')) str = p + 1;
// There is one caveat: if the variable is an object, its constructor is invoked every time
// it enters scope and is created, and its destructor is invoked every time it goes out of scope.
// Inefficient implementation:
for (int i = 0; i < 1000000; ++i) {
Foo f; // My ctor and dtor get called 1000000 times each.
f.DoSomething(i);
}
// It may be more efficient to declare such a variable used in a loop outside that loop:
Foo f; // My ctor and dtor get called once each.
for (int i = 0; i < 1000000; ++i) {
f.DoSomething(i);
}
Static and Global Variables
Classes 类
Doing Work in Constructors
- 绝不能调用虚函数 Avoid virtual method calls in constructors
- 决不能调用可能失败的操作 avoid initialization that can fail if you can’t signal an error.
- 可以把上面的操作添加到一个
Init()
函数中, 注意 Init 不要滥用 - 推荐使用 factory function, 具体理由参见 TotW #42 类似这样
// foo.h
class Foo {
public:
// Factory method: creates and returns a Foo.
// May return null on failure.
static std::unique_ptr<Foo> Create();
// Foo is not copyable.
Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;
private:
// Clients can't invoke the constructor directly.
Foo();
};
// foo.c
std::unique_ptr<Foo> Foo::Create() {
// Note that since Foo's constructor is private, we have to use new.
return absl::WrapUnique(new Foo());
}
Implicit Conversions
- 单参数构造函数必须使用
explicit
- 特例: copy and move constructors should not be explicit since they do not perform type conversion
- 特例: Implicit conversions can sometimes be necessary and appropriate for types that are designed to transparently wrap other types. In that case, contact your project leads to request a waiver of this rule.
- Constructors that cannot be called with a single argument may omit explicit.
- Constructors that take a single std::initializer_list parameter should also omit explicit, in order to support copy-initialization (e.g. MyType m = {1, 2}????.
Copyable and Movable Types
- A movable type is one that can be initialized and assigned from temporaries.
- A copyable type is one that can be initialized or assigned from any other object of the same type (so is also movable by definition), with the stipulation that the value of the source does not change.
- int and string are examples of movable types that are also copyable.
-
std::unique_ptr<int>
is an example of a movable but not copyable type (since the value of the sourcestd::unique_ptr<int>
must be modified during assignment to the destination) - A class’s public API should make explicit whether the class is copyable, move-only, or neither copyable nor movable. Support copying and/or moving if these operations are clear and meaningful for your type.
- 怎么限制, 以以下的代码形式
class Copyable {
public:
Copyable(const Copyable& rhs) = default;
Copyable& operator=(const Copyable& rhs) = default;
// The implicit move operations are suppressed by the declarations above.
};
class MoveOnly {
public:
MoveOnly(MoveOnly&& rhs);
MoveOnly& operator=(MoveOnly&& rhs);
// The copy operations are implicitly deleted, but you can
// spell that out explicitly if you want:
MoveOnly(const MoveOnly&) = delete;
MoveOnly& operator=(const MoveOnly&) = delete;
};
class NotCopyableOrMovable {
public:
// Not copyable or movable
NotCopyableOrMovable(const NotCopyableOrMovable&) = delete;
NotCopyableOrMovable& operator=(const NotCopyableOrMovable&)
= delete;
// The move operations are implicitly disabled, but you can
// spell that out explicitly if you want:
NotCopyableOrMovable(NotCopyableOrMovable&&) = delete;
NotCopyableOrMovable& operator=(NotCopyableOrMovable&&)
= delete;
};
简写版, 在类的private中添加空的拷贝构造函数和赋值操作,并且只有声明,不进行定义
// the old way
#define DISALLOW_COPY_AND_ASSIGN(Type) \
Type(const Type&); \
void operator = (const Type&)
class Foo
{
public:
Foo(int f);
~Foo();
private:
DISALLOW_COPY_AND_ASSIGN(Foo);
};
Structs vs. Classes
-
Struct
只用来保存数据组合, 不能带有任何函数 - 其他所有情况都用
class
- 特例 : For consistency with STL, you can use struct instead of class for functors and traits.
Inheritance
- 组合比继承更好 Composition is often more appropriate than inheritance.
-
is-a
情况下可以使用继承. Try to restrict use of inheritance to the “is-a” case: Bar subclasses Foo if it can reasonably be said that Bar “is a kind of” Foo. - 必须使用继承时, 确保是
public
继承. When using inheritance, make it public. - data 始终是private 的, 可以被子类访问的 method 用 protected : Limit the use of protected to those member functions that might need to be accessed from subclasses
- 重定义派生的虚函数时,在派生类中明确声明其为virtual : Explicitly annotate overrides of virtual functions or virtual destructors with exactly one of an override or (less frequently) final specifier. Do not use virtual when declaring an override. Rationale: A function or destructor marked override or final that is not an override of a base class virtual function will not compile, and this helps catch common errors. The specifiers serve as documentation; if no specifier is present, the reader has to check all ancestors of the class in question to determine if the function or destructor is virtual or not.
- Multiple inheritance is permitted, but multiple implementation inheritance is strongly discouraged. 可以考虑使用接口, 纯接口必须以Interface为后缀, 如
RunnableInterface
Operator Overloading
- Overload operators judiciously. Do not create user-defined literals.
- Define overloaded operators only if their meaning is obvious, unsurprising, and consistent with the corresponding built-in operators. For example, use | as a bitwise- or logical-or, not as a shell-style pipe.
- prefer to define ==, =, and <<, rather than Equals(), CopyFrom(), and PrintTo().
- Do not overload &&, ||, , (comma), or unary &. Do not overload operator"", i.e. do not introduce user-defined literals.
- don’t define operator overloads just because other libraries expect them. For example, if your type doesn’t have a natural ordering, but you want to store it in a std::set, use a custom comparator rather than overloading <.
Access Control
- Make classes’ data members private, unless they are static const
Declaration Order
- Group similar declarations together, placing public parts earlier.
- A class definition should usually start with a public: section, followed by protected:, then private:. Omit sections that would be empty.
- Within each section, generally prefer grouping similar kinds of declarations together, and generally prefer the following order:
- types (including typedef, using, and nested structs and classes),
- constants,
- factory functions,
- constructors,
- assignment operators,
- destructor,
- all other methods,
- data members.
- Do not put large method definitions inline in the class definition. Usually, only trivial or performance-critical, and very short, methods may be defined inline.
Functions 函数
Output Parameters
- The output of a C++ function is naturally provided via a return value and sometimes via output parameters.
- Prefer using return values instead of output parameters since they improve readability and oftentimes provide the same or better performance.
- If output-only parameters are used they should appear after input parameters.
- Input parameters are usually values or const references, while output and input/output parameters will be pointers to non-const.
- When ordering function parameters, put all input-only parameters before any output parameters.
- Parameters that are both input and output (often classes/structs) muddy the waters, and, as always, consistency with related functions may require you to bend the rule.
Write Short Functions
- Prefer small and focused functions.
- 一般而言, 超过 40 行的代码就需要考虑拆分了.
- Even if your long function works perfectly now, someone modifying it in a few months may add new behavior. This could result in bugs that are hard to find. Keeping your functions short and simple makes it easier for other people to read and modify your code.
Reference Arguments
- All parameters passed by lvalue reference must be labeled const.
void Foo(const string &in, string *out);
- However, there are some instances where using
const T*
is preferable toconst T&
for input parameters. For example:- You want to pass in a null pointer.
- The function saves a pointer or reference to the input.
Function Overloading
- Use overloaded functions (including constructors) only if a reader looking at a call site can get a good idea of what is happening without having to first figure out exactly which overload is being called.
- 比如下面这种情况可以采用, 但是更高效的方法是调用
std::string_view
.
class MyClass {
public:
void Analyze(const string &text);
void Analyze(const char *text, size_t textlen);
};
- You may overload a function when there are no semantic differences between variants. These overloads may vary in types, qualifiers, or argument count. However, a reader of such a call must not need to know which member of the overload set is chosen, only that something from the set is being called. If you can document all entries in the overload set with a single comment in the header, that is a good sign that it is a well-designed overload set.
Default Arguments
- Default arguments are banned on virtual functions, where they don’t work properly,
- Default arguments are allowed on non-virtual functions when the default is guaranteed to always have the same value.
- When in doubt, use overloads.
Trailing Return Type Syntax
C++ 11 引入了新的函数声明方法,
auto foo(int x) -> int;
- 仅在必须使用上述方式的地方使用这种方式, 如 lambda
- 大多数情况下, 仍然采用传统方式.
int foo(int x);
Google-Specific Magic
Ownership and Smart Pointers
-
Ownership
is a bookkeeping technique for managing dynamically allocated memory (and other resources). The owner of a dynamically allocated object is an object or function that is responsible for ensuring that it is deleted when no longer needed. Ownership can sometimes be shared, in which case the last owner is typically responsible for deleting it. Even when ownership is not shared, it can be transferred from one piece of code to another. - “Smart” pointers are classes that act like pointers, e.g. by overloading the * and -> operators. Some smart pointer types can be used to automate ownership bookkeeping, to ensure these responsibilities are met.
-
std::unique_ptr
is a smart pointer type introduced in C++11, which expresses exclusive ownership of a dynamically allocated object; the object is deleted when the std::unique_ptr goes out of scope. It cannot be copied, but can be moved to represent ownership transfer. -
std::shared_ptr
is a smart pointer type that expresses shared ownership of a dynamically allocated object. std::shared_ptrs can be copied; ownership of the object is shared among all copies, and the object is deleted when the last std::shared_ptr is destroyed. - Never use
std::auto_ptr
. - Prefer to use
std::unique_ptr
to make ownership transfer explicit, 例如
std::unique_ptr<Foo> FooFactory();
void FooConsumer(std::unique_ptr<Foo> ptr);
cpplint
- Use
cpplint.py
to detect style errors. -
cpplint.py is a tool that reads a source file and identifies many style errors. It is not perfect, and has both false positives and false negatives, but it is still a valuable tool. False positives can be ignored by putting
// NOLINT
at the end of the line or// NOLINTNEXTLINE
in the previous line.
Other C++ Features
Rvalue References
- Rvalue references are a type of reference that can only bind to temporary objects. The syntax is similar to traditional reference syntax. For example,
void f(string&& s);
declares a function whose argument is an rvalue reference to a string. - You may use rvalue references to define move constructors and move assignment operators
Friends
- We allow use of
friend
classes and functions, within reason. - Friends should usually be defined in the same file so that the reader does not have to look in another file to find uses of the private members of a class.
- A common use of friend is to have a FooBuilder class be a friend of Foo so that it can construct the inner state of Foo correctly, without exposing this state to the world.
- In some cases it may be useful to make a unittest class a friend of the class it tests.
Exceptions
- We do not use C++ exceptions.
noexcept
- The noexcept specifier is used to specify whether a function will throw exceptions or not. If an exception escapes from a function marked noexcept, the program crashes via std::terminate.
- The noexcept operator performs a compile-time check that returns true if an expression is declared to not throw any exceptions.
- Specify
noexcept
when it is useful and correct.
Run-Time Type Information(RTTI)
- Avoid using RTTI
- RTTI allows a programmer to query the C++ class of an object at run time. This is done by use of typeid or dynamic_cast.
Casting
- Use C+±style casts like
static_cast<float>(double_value)
- Or brace initialization for conversion of arithmetic types like
int64 y = int64{1} << 42
- Do not use C-style cast formats like
int y = (int)x
orint y = int(x)
- Decisions :
- Use brace initialization to convert arithmetic types (e.g. int64{x}). This is the safest approach because code will not compile if conversion can result in information loss. The syntax is also concise.
- Use
static_cast
as the equivalent of a C-style cast that does value conversion, when you need to explicitly up-cast a pointer from a class to its superclass, or when you need to explicitly cast a pointer from a superclass to a subclass. In this last case, you must be sure your object is actually an instance of the subclass. - Use
const_cast
to remove the const qualifier - Use
reinterpret_cast
to do unsafe conversions of pointer types to and from integer and other pointer types. Use this only if you know what you are doing and you understand the aliasing issues.
Streams
- Streams are the standard I/O abstraction in C++, as exemplified by the standard header
<iostream>
. They are widely used in Google code, but only for debug logging and test diagnostics. - Use streams where appropriate, and stick to “simple” usages. Overload << for streaming only for types representing values, and write only the user-visible value, not any implementation details.
Preincrement and Predecrement
- Use prefix form
(++i)
of the increment and decrement operators with iterators and other template objects.
Use of const
- Use
const
whenever it makes sense. With C++11,constexpr
is a better choice for some uses ofconst
.
Use of constexpr
- Some variables can be declared
constexpr
to indicate the variables are true constants, i.e. fixed at compilation/link time. Some functions and constructors can be declared constexpr which enables them to be used in defining a constexpr variable. - constexpr definitions enable a more robust specification of the constant parts of an interface.
- Use constexpr to specify true constants and the functions that support their definitions.
- 不要滥用 Avoid complexifying function definitions to enable their use with constexpr.
- Do not use constexpr to force inlining.
Integer Types
- C++ 本身未定义数据的位数. C++ does not specify the sizes of integer types like int. Typically people assume that short is 16 bits, int is 32 bits, long is 32 bits and long long is 64 bits.
- 一般的小的数字, 心中有数不会太出格的数, 用
int
. 比如循环 - 其余的, 需要比较精确的限定整型数大小范围的, 使用
stdint.h
中定义的int16_t
,uint32_t
,int64_t
等. - 尽量避免使用
unsigned
64-bit Portabaility
- Code should be 64-bit and 32-bit friendly. Bear in mind problems of
printing
,comparisons
, andstructure alignment
- Remember that
sizeof(void *) != sizeof(int)
. Useintptr_t
if you want a pointer-sized integer.
Preprocessor Macros
- Avoid defining macros, especially in headers;
- prefer inline functions, enums, and const variables. Name macros with a project-specific prefix.
- Do not use macros to define pieces of a C++ API.
0 and nullptr/NULL
Use
-
0
for integers -
0.0
for reals -
nullptr
for void pointers,NULL
for C++ 03 or earlier -
\\0
for null chars.
sizeof
- Prefer
sizeof(varname)
tosizeof(type)
.
auto
- Use auto to avoid type names that are noisy, obvious, or unimportant - cases where the type doesn’t aid in clarity for the reader. Continue to use manifest type declarations when it helps readability.
for (const auto& item : some_map) {
const KeyType& key = item.first;
const ValType& value = item.second;
// The rest of the loop can now just refer to key and value,
// a reader can see the types in question, and we've avoided
// the too-common case of extra copies in this iteration.
}
Braced Initializer List
- In C++ 03, aggregate types (arrays and structs with no constructor) could be initalized with braced initializer list, like
struct Point {int x; int y};
Point p = {1,2};
- In C++ 11, this syntax was generalized to all object type, known as
braced-init-list
, i.e.
//Vector takes a braced-init-list of elements
std::vector<string> v{"foo", "bar"};
//Basically the same.
std::vector<string> v = {"foo", "bar"};
//Usable with `new` expressions
auto p = new std::vector<string>{"foo", "bar"};
// A map can take a list of pairs. Nested braced-init-list work
std::map<int, string> m = {{1, "one"},{2, "two"}};
// A braced-init-list can be implicitly converted to a return type
std::vector<int> test_function() {return {1,2,3};}
// Iterate over a braced-init-list
for(int i : {-1,-2,-3}) {}
//Call a function using a braced-init-list
void TestFunction2(std::vector<int> v) {}
TestFunction2({1,2,3});
- A user-defined type can also define a constuctor and/or assignment operator that take
std::initializer_list<T>
, which is automatically created frombraced-init-list
.
class MyType {
public:
//std::initializer_list references the underlying init list.
//It should be passed by value
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 m1{1,2,3};
MyType m2 = {4,5,6};
- Finally, brace initialization can also call ordinary constructors of data type, even if they do not have
std::initalizer_list<T>
constructors.
// Calls ordinary constuctors as long as MyOtherType has no
// std::initializer_list constructor.
class MyOtherType {
public:
explicit MyOtherType(string);
MyOtherType(int, string);
};
MyOtherType m = {1, "one"};
// If the constructor is explicit, you can't use the "= {}" form
MyOtherType m2{"b"};
- warning : Nerver assign a braced-init-list to an auto local variable.
auto d = {1.23}; //bad! d is a std::initializer_list<double>
auto d = double{1.23}; //Good! d is a double
Lambda Expressions
- Lambda expressions are a concise way of creating anonymous function objects.
- They’re often useful when passing functions as arguments.
- Lambdas were introduced in C++11 along with a set of utilities for working with function objects, such as the polymorphic wrapper
std::function
. - They further allow capturing variables from the enclosing scope either explicitly by name, or implicitly using a default capture. Explicit captures require each variable to be listed, as either a value or reference capture
// A simple lambda expression
std::sort(v.begin(), v.end(), [](int x, int y) {
return Weight(x) < Weight(y);
});
// Good : explicit capture
{
Foo foo;
...
executor->Schedule([&foo] { Frobnicate(foo); })
...
}
// BETTER - The compile will fail if `Frobnicate` is a member
// function, and it's clearer that `foo` is dangerously captured by
// reference.
// Bad : implicit capture
{
Foo foo;
...
executor->Schedule([&] { Frobnicate(foo); })
...
}
// BAD! The fact that the lambda makes use of a reference to `foo` and
// possibly `this` (if `Frobnicate` is a member function) may not be
// apparent on a cursory inspection. If the lambda is invoked after
// the function returns, that would be bad, because both `foo`
// and the enclosing object could have been destroyed.
Template metaprogramming
- Template metaprogramming refers to a family of techniques that exploit the fact that the C++ template instantiation mechanism is Turing complete and can be used to perform arbitrary compile-time computation in the type domain.
- Template metaprogramming sometimes allows cleaner and easier-to-use interfaces than would be possible without it, but it’s also often
a temptation to be overly clever
. It’s best used in a small number of low level components where the extra maintenance burden is spread out over a large number of uses. - Avoid complicated template programming.
Boost
- The
Boost library collection
is a popular collection of peer-reviewed, free, open-source C++ libraries. - Use only approved libraries from the Boost library collection.
-
Call Traits
fromboost/call_traits.hpp
-
Compressed Pair
fromboost/compressed_pair.hpp
-
The Boost Graph Library (BGL)
fromboost/graph
, except serialization (adj_list_serialize.hpp) and parallel/distributed algorithms and data structures (boost/graph/parallel/* and boost/graph/distributed/*). -
Property Map
fromboost/property_map
, except parallel/distributed property maps (boost/property_map/parallel/*). -
Iterator
fromboost/iterator
- The part of
Polygon
that deals with Voronoi diagram construction and doesn’t depend on the rest of Polygon:boost/polygon/voronoi_builder.hpp
,boost/polygon/voronoi_diagram.hpp, and boost/polygon/voronoi_geometry_type.hpp
-
Bimap
fromboost/bimap
-
Statistical Distributions and Functions
fromboost/math/distributions
-
Special Functions
fromboost/math/special_functions
-
Multi-index
fromboost/multi_index
-
Heap
fromboost/heap
- The flat containers from
Container
:boost/container/flat_map
, andboost/container/flat_set
-
Intrusive
fromboost/intrusive.
- The
boost/sort
library. -
Preprocessor
fromboost/preprocessor
.
-
std::hash
-
std::hash<T>
is the function object that the C++11 hash containers use to hash keys of type T, unless the user explicitly specifies a different hash function. For example,std::unordered_map<int, string>
is a hash map that usesstd::hash<int>
to hash its keys, whereasstd::unordered_map<int, string, MyIntHash>
uses MyIntHash. -
std::hash
is defined for all integral, floating-point, pointer, and enum types, as well as some standard library types such as string andunique_ptr
. Users can enable it to work for their own types by defining specializations of it for those types. -
Do not define specializations of
std::hash
.
C++ 11
- Use libraries and language extensions from C++11 when appropriate.
- Consider portability to other environments before using C++11 features in your project.
- the following C++11 features may not be used
- Compile-time rational numbers (
<ratio>
), because of concerns that it’s tied to a more template-heavy interface style. - The
<cfenv>
and<fenv.h>
headers, because many compilers do not support those features reliably.
- Compile-time rational numbers (
Nonstandard Extensions
- Nonstandard extensions to C++ may not be used unless otherwise specified.
Aliases
- 在 C++ 中, 可以用以下方式创建 aliases
typedef Foo Bar;
using Bar = Foo;
using other_namespace::Foo;
- In new code, using is preferable to typedef, because it provides a more consistent syntax with the rest of C++ and works with templates.
- Good ones
namespace mynamespace {
// Used to store field measurements. DataPoint may change from Bar* to some internal type.
// Client code should treat it as an opaque pointer.
using DataPoint = foo::Bar*;
// A set of measurements. Just an alias for user convenience.
using TimeSeries = std::unordered_set<DataPoint, std::hash<DataPoint>, DataPointComparator>;
} // namespace mynamespace
- Bad ones
namespace mynamespace {
// Bad: none of these say how they should be used.
using DataPoint = foo::Bar*;
using std::unordered_set; // Bad: just for local convenience
using std::hash; // Bad: just for local convenience
typedef unordered_set<DataPoint, hash<DataPoint>, DataPointComparator> TimeSeries;
} // namespace mynamespace
- local convenience aliases are fine in function definitions, private sections of classes, explicitly marked internal namespaces, and in .cc files:
// In a.cc file
using foo::Bar;
Naming 命名
命名风格
- 最重要的是连贯性.
The rules are the rules!
- 其次是清晰性.
一般规则
- 名字是要达意的, 尽量不要用缩写, 以避免增加理解难度.
- As a rule of thumb, an abbreviation is probably OK if it’s listed in Wikipedia.
- Note that certain universally-known abbreviations are OK, such as
i
for an iteration variable andT
for a template parameter.
Good ones :
int price_count_reader; // No abbreviation.
int num_errors; // "num" is a widespread convention.
int num_dns_connections; // Most people know what "DNS" stands for.
int lstm_size; // "LSTM" is a common machine learning abbreviation.
Bad ones
int n; // Meaningless.
int nerr; // Ambiguous abbreviation.
int n_comp_conns; // Ambiguous abbreviation.
int wgc_connections; // Only your group knows what this stands for.
int pc_reader; // Lots of things can be abbreviated "pc".
int cstmr_id; // Deletes internal letters.
FooBarRequestInfo fbri; // Not even a word.
File Names
- 全小写
-
_
连 - 成对儿出.
- Be unique.
http_server_logs.h
rather thanlog.h
//For a class called MyUsefulClass
my_useful_class.h
my_useful_class.cc
Type Names
- Type :
classes, structs, type aliases, enums, and type template parameters
- Rules : Type names should
start with a capital letter
andhave a capital letter for each new word
.No underscores
.
// classes and structs
class UrlTable { ...
class UrlTableTester { ...
struct UrlTableProperties { ...
// typedefs
typedef hash_map<UrlTableProperties *, string> PropertiesMap;
// using aliases
using PropertiesMap = hash_map<UrlTableProperties *, string>;
// enums
enum UrlTableErrors { ...
Variable Names
- 一般变量, lowcase +
_
string table_name; // OK - uses underscore.
string tablename; // OK - all lowercase.
string tableName; // Bad - mixed case.
- const 常量,
k
打头, Underscores can be used as separators in the rare cases where capitalization cannot be used for separation.
const int kDaysInAWeek = 7;
const int kAndroid8_0_0 = 24; // Android 8.0.0
Class Data Members
- ends with
_
class TableInfo {
...
private:
string table_name_; // OK - underscore at end.
string tablename_; // OK.
static Pool<TableInfo>* pool_; // OK.
};
Struct Data Members
- no
_
like class
struct UrlTableProperties {
string name;
int num_entries;
static Pool<UrlTableProperties>* pool;
};
Function Names
一般地,
AddTableEntry()
DeleteUrl()
OpenFileOrDie()
特殊的, setters & getters could be named like variables.
int count() const;
void set_count(int count)
Namespace Names
- Namespace names are all lower-case.
- Top-level namespace names are based on the project name .
- Avoid collisions between nested namespaces and well-known top-level namespaces.
Macro Names
- 尽量不用
- 用的话,
capitals
+_
#define ROUND(x) ...
#define PI_ROUNDED 3.0
Enumerator Names
- like
macros
, 但要竭力避免名字冲突. Google 现在推荐 按照 const 的方式写,但是难以接受
enum AlternateUrlTableErrors {
OK = 0,
OUT_OF_MEMORY = 1,
MALFORMED_INPUT = 2,
};
- 或许可以加上前缀
enum AlternateUrlTableErrors {
ENUM_ALTERNATED_URL_TABLE_EROOR_STATE_OK = 0,
};
Exceptions to Naming Rules
正在使用一个库, 那么可以 follow 它的命名方式.
sparse_hash_map // STL-like entity; follows STL naming conventions
Comments 注释
注释风格
- 统一风格,只用
//
. - 注意对齐和美观.
- 不要写废话, 尽量别BB
Bad
// Find the element in the vector. <-- Bad: obvious!
auto iter = std::find(v.begin(), v.end(), element);
if (iter != v.end()) {
Process(element);
}
Good
// Process "element" unless it was already processed.
auto iter = std::find(v.begin(), v.end(), element);
if (iter != v.end()) {
Process(element);
}
Best
if (!IsAlreadyProcessed(element)) {
Process(element);
}
文件注释
在文件的开头加上版权公告, 然后是文件内容描述.
// Copyright 2018 Jingxi Inc.
// Licences (MIT / BSD / ...)
// Author : Shiye (aaa@qq.com)
//
// This is ...
类注释
类的注释要写清楚类的功能
, 用法
, 和 注意事项
, 最好能给一个小栗子.
// Iterates over the contents of a GargantuanTable.
// Example:
// GargantuanTableIterator* iter = table->NewIterator();
// for (iter->Seek("foo"); !iter->done(); iter->Next()) {
// process(iter->key(), iter->value());
// }
// delete iter;
class GargantuanTableIterator {
...
};
函数注释
函数声明 declaration
在函数声明处描述函数的功能, 信息包括
- What the inputs and outputs are.
- For class member functions: whether the object remembers reference arguments beyond the duration of the method call, and whether it will free them or not.
- If the function allocates memory that the caller must free.
- Whether any of the arguments can be a null pointer.
- If there are any performance implications of how a function is used.
- If the function is re-entrant. What are its synchronization assumptions?
// Returns an iterator for this table. It is the client's
// responsibility to delete the iterator when it is done with it,
// and it must not use the iterator once the GargantuanTable object
// on which the iterator was created has been deleted.
//
// The iterator is initially positioned at the beginning of the table.
//
// This method is equivalent to:
// Iterator* iter = table->NewIterator();
// iter->Seek("");
// return iter;
// If you are going to immediately seek to another place in the
// returned iterator, it will be faster to use NewIterator()
// and avoid the extra seek.
Iterator* GetIterator() const;
函数实现 implementation
在函数实现处描述函数的实现细节.
函数体 body
在函数体内, you should have comments in tricky
, non-obvious
, interesting
, or important parts
of your code,
- tricks
// Divides result by two, taking into account that x
// contains the carry from the add.
for (int i = 0; i < result->size(); i++) {
x = (x << 8) + (*result)[i];
(*result)[i] = x >> 1;
x &= 1;
}
- non-obvious code
// If we have enough memory, mmap the data portion too.
mmap_budget = max<int64>(0, mmap_budget - index_->length());
if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytes, mlock))
return; // Error already logged.
函数参数 parameters
当函数参数无法很好地 self-explain 时, 可以这样做
-
参数是一个任意的常量, ok , 起个名字, 变成 const 常量.
-
bool
变量用enum
变量代替, 直接甩一个true/false
一脸懵逼, 写成enum
带自解释就好了 -
多个参数, 不如定义为一个
class
或者struct
吧 -
如果是个太长的表达式, 拜托, 还是起个变量名吧
-
实在懒得动了, 那就在调用的时候用注释声明吧
-
Bad
> // What are these arguments?
const DecimalNumber product = CalculateProduct(values, 7, false, nullptr);
- Good
ProductOptions options;
options.set_precision_decimals(7);
options.set_use_cache(ProductOptions::kDontUseCache);
const DecimalNumber product =
CalculateProduct(values, options, /*completion_callback=*/nullptr);
TODO 注释
// TODO(aaa@qq.com): Use a "*" here for concatenation operator.
// TODO(Zeke) change this to use relations.
// TODO(bug 12345): remove the "Last visitors" feature
// TODO(bug 12345): Fix by November 2005
// TODO(bug 12345): Remove this code when all clients can handle XML responses
Formatting
Line Length
- 每行最长 80 个 char
Non-ASCII Characters
- Non-ASCII characters should be rare, and must use UTF-8 formatting.
Spaces vs. Tabs
- 只用 Spaces,
- 一次缩进 2 个
- 设置编辑器, hit Tab 也 print spaces
Function Declarations and Definitions
- 尽量一行, 多行对齐
- Unused parameters that are obvious from context may be omitted,
Foo(Foo&&)
- Unused parameters that might not be obvious should comment out the variable name in the function definition, like
void Circle::Rotate(double /*radians*/) {}
Lambda Expressions
- Format parameters and bodies as for any other function, and capture lists like other comma-separated lists.
// For by-reference captures, do not leave a space between the ampersand (&) and the variable name.
int x = 0;
auto x_plus_n = [&x](int n) -> int { return x + n; }
// Short lambdas may be written inline as function arguments.
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
- 尽量一行
- 分行对齐
- 重点突出, 可加注释
Braced Initializer List Format
- 同 Function Calls
Conditionals
- 短的, 一行搞定
if (condition) DoSomething();
- 复杂的, 注意对齐
if (condition) {
... //2 space indent
} else if (...) {
...
} else {
...
}
Loops and Switch Statements
- 一般 Switch 这样写
switch (var) {
case 0: { // 2 space indent
... // 4 space indent
break;
}
case 1: {
...
break;
}
default: {
assert(false);
}
}
- Fall-through 的, 加 macro,
ABSL_FALLTHROUGH_INTENDED
defined inabsl/base/macros.h
switch (x) {
case 41: // No annotation needed here.
case 43:
if (dont_be_picky) {
// Use this instead of or along with annotations in comments.
ABSL_FALLTHROUGH_INTENDED;
} else {
CloseButNoCigar();
break;
}
case 42:
DoSomethingSpecial();
ABSL_FALLTHROUGH_INTENDED;
default:
DoSomethingGeneric();
break;
}
- 小 for-loop,
for(int i = 0; i < kSomeNumber; ++i)
printf("I Love you \n");
- Empty loop bodies 要注意格式, 不要被误解
while (condition) {
// Repeat test until it returns false.
}
for (int i = 0; i < kSomeNumber; ++i) {} // Good - one newline is also OK.
while (condition) continue; // Good - continue indicates no logic.
while (condition); // Bad - looks like part of do/while loop.
Pointer and Reference Expresions
// Good Forms
x = *p;
p = &x;
x = r.y;
x = r->y;
// Either is OK, keep it consistently
const string &str;
const string& str;
char *c;
char* c;
// Bad!
int x, *y; // Disallowed - no & or * in multiple declaration
char * c; // Disallowed
const string & str; //Disallowed
Boolean Expressions
- Google style 逻辑符号后置, 我喜欢前置
if (this_one_thing > this_other_thing &&
a_third_thing == a_fourth_thing &&
yet_another && last_one) {
...
}
Return Values
return result; // No parentheses in the simple case.
// Parentheses OK to make a complex expression more readable.
return (some_long_condition &&
another_condition);
Variable and Array Initialization
int x = 3;
string name = "Some Name";
// Be careful
std::vector<int> v(100, 1); // A vector containing 100 items: All 1s.
std::vector<int> v{100, 1}; // A vector containing 2 items: 100 and 1.
int pi(3.14); // OK -- pi == 3.
int pi{3.14}; // Compile error: narrowing conversion.
Preprocessor Directives
-
#preprocessor
总是置于行首.
// Good - directives at beginning of line
if (lopsided_score) {
#if DISASTER_PENDING // Correct -- Starts at beginning of line
DropEverything();
# if NOTIFY // OK but not required -- Spaces after #
NotifyClient();
# endif
#endif
BackToNormal();
}
Class Format
class MyClass : public OtherClass {
public: // Note the 1 space indent!
MyClass(); // Regular 2 space indent.
explicit MyClass(int var);
~MyClass() {}
void SomeFunction();
void SomeFunctionThatDoesNothing() {
}
void set_some_var(int var) { some_var_ = var; }
int some_var() const { return some_var_; }
private:
bool SomeInternalFunction();
int some_var_;
int some_other_var_;
};
Constructor Initializer Lists
// When everything fits on one line:
MyClass::MyClass(int var) : some_var_(var) {
DoSomething();
}
// If the signature and initializer list are not all on one line,
// you must wrap before the colon and indent 4 spaces:
MyClass::MyClass(int var)
: some_var_(var), some_other_var_(var + 1) {
DoSomething();
}
// When the list spans multiple lines, put each member on its own line
// and align them:
MyClass::MyClass(int var)
: some_var_(var), // 4 space indent
some_other_var_(var + 1) { // lined up
DoSomething();
}
// As with any other code block, the close curly can be on the same
// line as the open curly, if it fits.
MyClass::MyClass(int var)
: some_var_(var) {}
Namespace Formatting
- The contents of namespaces are not indented.
namespace {
void foo() { // Correct. No extra indentation within namespace.
...
}
} // namespace
- When declaring nested namespaces, put each namespace on its own line
namespace foo {
namespace bar {
Horizontal Whitespace
- Use of horizontal whitespace depends on location. Never put trailing whitespace at the end of a line.
// General
oid f(bool b) { // Open braces should always have a space before them.
...
int i = 0; // Semicolons usually have no space before them.
// Spaces inside braces for braced-init-list are optional. If you use them,
// put them on both sides!
int x[] = { 0 };
int x[] = {0};
// Spaces around the colon in inheritance and initializer lists.
class Foo : public Bar {
public:
// For inline function implementations, put spaces between the braces
// and the implementation itself.
Foo(int b) : Bar(), baz_(b) {} // No spaces inside empty braces.
void Reset() { baz_ = 0; } // Spaces separating braces from implementation.
...
// Loops and Conditionals
if (b) { // Space after the keyword in conditions and loops.
} else { // Spaces around else.
}
while (test) {} // There is usually no space inside parentheses.
switch (i) {
for (int i = 0; i < 5; ++i) {
// Range-based for loops always have a space before and after the colon.
for (auto x : counts) {
...
}
switch (i) {
case 1: // No space before colon in a switch case.
...
case 2: break; // Use a space after a colon if there's code after it.
// Operators
// Assignment operators always have spaces around them.
x = 0;
// Other binary operators usually have spaces around them, but it's
// OK to remove spaces around factors. Parentheses should have no
// internal padding.
v = w * x + y / z;
v = w*x + y/z;
v = w * (x + z);
// No spaces separating unary operators and their arguments.
x = -5;
++x;
if (x && !y)
// Templates and Casts
// No spaces inside the angle brackets (< and >), before
// <, or between >( in a cast
std::vector<string> x;
y = static_cast<char*>(x);
// Spaces between type and pointer are OK, but be consistent.
std::vector<char *> x;
Vertical Whitespace
- Minimize use of vertical whitespace. The more code that fits on one screen, the easier it is to follow and understand the control flow of the program. Use whitespace purposefully to provide separation in that flow.
- some rules
- Blank lines at the beginning or end of a function do not help readability.
- Blank lines inside a chain of if-else blocks may well help readability.
- A blank line before a comment line usually helps readability
Exceptions to the Rules
Existing Non-conformant Code
Windows Code
- Use the Google naming conventions,including the
.cc
extention for source file - 尽量少用 Windows 专属 特性, 尽量靠近标准 C++
- 当使用 VC++ 编译的时候, 设置编译器
warning level 3
or higher, treat all warnings as errors. =>0 warnings, 0 errors
- 不使用
#pragma once
, 使用include guard
Parting Words : Use common sense and BE CONSISTENT
III. 专题深入, 积沙成塔
Ref
- Google C++ Style Guide : 官方原版
- C++ Coding Style : 不错的表格汇总
- Google C++ 风格指南: 很完整的中文翻译
下一篇: STM32串口通讯