C++ Skills Series - Coding Standards (Google C++ Programming Style Guide)

Insert image description here

现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。
Now everything is for the future of dream weaving wings, let the dream fly in reality.

Coding standards (Google C++ Style Guide)

Coding standard (Google C++ Style Guide) example

Insert image description here

0.Background

Most of Google's open source projects are developed using C++. Every C++ programmer also knows that C++ has many powerful language features, but this power inevitably leads to its complexity. This complexity will make the code more prone to bugs and difficult to read and maintain.
The purpose of this guide is to circumvent its complexity by detailing what to do and what not to do when coding in C++. These rules make it easy to manage while allowing code to use C++ language features efficiently.
Style, also known as readability, mainly refers to the habits that govern C++ code. Using the term style is a bit of a misnomer, as these habits go far beyond source code file formats.
One way to make your code manageable is to enhance code consistency. It is important to make your code understandable to others. Maintaining a unified programming style means that the meaning of various symbols can be easily inferred based on "pattern matching" rules. Creating common, necessary idioms and patterns can make the code easier to understand. In some cases, changing some programming styles may be a good choice, but we should still follow the principle of consistency and try not to do this.
Another point made in this guide is the feature bloat of C++. C++ is a giant language that contains a lot of advanced features. In some cases, we will restrict or even prohibit the use of certain features to simplify the code and avoid various problems that may cause it. The guide lists such features and explains why These features are of limited use.
Open source projects developed by Google will comply with these guidelines.
Note: This guide is not a C++ tutorial, we assume that the reader is already familiar with C++.

1.Header file

Usually, every .cc file (C++ source file) has a corresponding .h file (C++ header file), there are some exceptions, such as unit test code and .cc files that only contain main().
Proper use of header files can greatly improve code readability, file size, and performance.
The following rules will guide you to avoid various troubles when using header files.

1.1. Protection of #define

All header files should use #define to prevent multiple inclusions of header files. The naming format should be: HTo
ensure uniqueness, the header file should be named based on the full path of the project source code tree in which it is located. For example, the header file foo/src/bar/baz.h in project foo is protected as follows:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_

#endif // FOO_BAR_BAZ_H_

1.2. Header file dependencies

Use forward declarations to minimize the number of #includes in .h files.
When a header file is included, a new dependency is also introduced. As long as the header file is modified, the code must be recompiled. If your header file includes other header files, any changes to those header files will also cause the code that includes your header file to be recompiled. Therefore, we prefer to include as few header files as possible, especially those included within other header files.
Using forward declarations can significantly reduce the number of header files that need to be included. For example: the class File is used in the header file, but there is no need to access the File declaration, then the class File only needs to be pre-declared in the header file; there is no need to #include "file/base/file.h".
How can I use class Foo in a header file without accessing the class definition?

  1. Declare the data member type as Foo * or Foo &;
  2. Functions with parameters and return value types of Foo are only declared (but the implementation is not defined);
  3. The type of static data members can be declared as Foo because static data members are defined outside the class definition.
    On the other hand, if your class is a subclass of Foo, or contains non-static data members of type Foo, you must include a header file for it.
    Sometimes it does make sense to use pointer members (better yet scoped_ptr) instead of object members. However, this approach will reduce code readability and execution efficiency. If it is just to include less header files, it is better not to replace it like this.
    Of course, the .cc file needs the definition part of the class used anyway, and naturally it will contain several header files.
    Translator's note: Don't rely on definitions if you can rely on declarations.

1.3.Inline functions

Only when a function has 10 lines or less is it defined as an inline function.
Definition: When a function is declared as an inline function, the compiler may expand it inline without calling the inline function according to the usual function calling mechanism.
Advantages: When the function body is relatively small, inlining the function can make the target code more efficient. For access functions (accessor, mutator) and other short key execution functions.
Disadvantages: Abuse of inlining will cause the program to slow down. Inlining may increase or decrease the amount of target code, depending on the size of the function being inlined. Inlining smaller access functions usually reduces code size, but inlining a very large function (Translator's Note: If the compiler allows it) will dramatically increase code size. On modern processors, smaller code tends to execute faster due to better utilization of the instruction cache.
Conclusion: A good rule of thumb is not to inline functions that exceed 10 lines. Destructors should be treated with caution, destructors are often longer than they appear because of some implicit members and the base class destructor (if any) is called!
Another useful rule of thumb: it is no good to inline functions that contain loops or switch statements, unless in most cases these loops or switch statements are never executed.
Importantly, virtual and recursive functions are not necessarily inline functions even if they are declared inline. Generally, recursive functions should not be declared inline. recursive function). The main reason for inlining the destructor is that it is defined in the class definition, for convenience or to document its behavior.

1.4.-inl.h file

The definition of complex inline functions should be placed in a header file with the suffix name -inl.h.
Giving the definition of an inline function in a header file allows the compiler to expand it inline at the call site. However, the implementation code should be placed entirely in the .cc file. We do not want too much implementation code to appear in the .h file unless there are obvious advantages in readability and efficiency.
If the definition of the inline function is relatively short and the logic is relatively simple, its implementation code can be placed in the .h file. For example, the implementation of access functions is naturally placed in the class definition. For the convenience of implementation and calling, more complex inline functions can also be placed in .h files. If you feel that this will make the header file look bulky, you can also separate it into a separate -inl.h. This separates the implementation from the class definition and includes -inl.h where the implementation is located when needed.
The -inl.h file can also be used to define function templates, thereby making the template definition more readable.
One thing to remind is that -inl.h, like other header files, also needs #define protection.

1.5. Function Parameter Ordering

When defining a function, the parameter order is: input parameters first, output parameters last.
C/C++ function parameters are divided into two types: input parameters and output parameters. Sometimes input parameters are also output (Translator's Note: When the value is modified). Input parameters are generally passed by value or constant reference (const references), and output parameters or input/output parameters are non-const pointers (non-const pointers). When sorting parameters, place all input parameters before output parameters. Don't put it last just because it's a newly added parameter, but it should still be placed before the output parameters.
This is not a rule that must be followed, and mixed input/output parameters (usually class/structure variables) will make the rule difficult to follow.

1.6. Include the name and order of files

Standardizing the order of inclusion can enhance readability and avoid hidden dependencies (Translator's Note: Hidden dependencies mainly refer to included files during compilation). The order is as follows: C library, C++ library, .h of other libraries, projects .h inside.
Header files within the project should be arranged according to the project source code directory tree structure, and avoid using UNIX file paths. (current directory) and ... (parent directory). For example, google-awesome-project/src/base/logging.h should be included like this:
#include "base/logging.h"
The main role of dir/foo.cc is to execute or test the functionality of dir2/foo2.h, The order of header files included in foo.cc is as follows:
dir2/foo2.h (priority position, details are as follows)
C system files
C++ system files
Other library header files This sorting method of
header files in this project can effectively reduce hidden dependencies. We hope
Each header file is compiled independently. The simplest way to achieve this is to include it as the first .h file in the corresponding .cc.
dir/foo.cc and dir2/foo2.h are usually located in the same directory (like base/basictypes_unittest.cc and base/basictypes.h), but can also be in different directories.
Alphabetical order of header files in the same directory is a good choice.
For example, the inclusion order of google-awesome-project/src/foo/internal/fooserver.cc is as follows:
#include "foo/public/fooserver.h" // Priority
#include <sys/types.h>
#include <unistd.h>
#include <hash_map>
#include
#include “base/basictypes.h”
#include “base/commandlineflags.h”
#include “foo/public/bar.h”

1.7.Summary

  1. Avoiding multiple inclusions is the most basic requirement when learning programming;
  2. The forward declaration is to reduce compilation dependencies and prevent modification of a header file from causing a domino effect;
  3. Reasonable use of inline functions can improve code execution efficiency;
  4. -inl.h can improve code readability (generally not used :D);
  5. Standardizing the order of function parameters can improve readability and ease of maintenance (it has a slight impact on the stack space of function parameters, I used to put them mostly of the same type together);
  6. The names of included files use . and... although convenient but prone to confusion. Using a relatively complete project path looks very clear and organized. In addition to being beautiful, the order of included files, the most important thing is that it can reduce hidden dependencies and make each header file When compiling in the place that "most needs compilation" (corresponding to the source file: D), some people suggest that the library file should be placed at the end. In this way, the files in the project will go wrong first, and the header files will be placed at the front of the corresponding source file. This is enough to ensure Internal errors were discovered promptly.

2.Scope

2.1. Namespaces

In .cc files, it is recommended to use unnamed namespaces (Translator's Note: Unnamed namespaces are like unnamed classes, and they seem to be rarely introduced:-(). When using named namespaces , its name can be based on the project or path name, do not use the using indicator.
Definition: Namespace subdivides the global scope into different, named scopes, which can effectively prevent naming conflicts in the global scope. Advantages
: Namespace provides Of course, the class also provides a (nested) name axis (Translator's Note: Split the name in different namespaces). class).
For example, two different projects have a class Foo in the global scope, causing conflicts at compile or run time. If each project places the code in a different namespace, project1::Foo Naturally there will be no conflict with project2::Foo as different symbols.
Disadvantages: Namespaces are confusing because they provide additional (nested) naming axes like classes. Using unnamed spaces in header files is easy to violate C++'s One Definition Rule (ODR).
Conclusion: Use namespaces appropriately according to the strategies mentioned below.

2.1.1.Unnamed Namespaces

In .cc files, the use of unnamed namespaces is allowed or even encouraged to avoid naming conflicts at runtime:
namespace { // In .cc files
// the contents of the namespace do not need to be indented
enum { UNUSED, EOF, ERROR }; / / Frequently used symbols
bool AtEof() { return pos_ == EOF; } // Use the symbol EOF within this namespace
} // namespace
However, file scope declarations associated with a specific class are declared as types in that class , a static data member or a static member function, rather than a member of an unnamed namespace. As shown above, unnamed namespaces are terminated with the // namespace comment.
Unnamed namespaces cannot be used in .h files.

2.1.2. Named Namespaces

The named namespace is used as follows:
the namespace encapsulates the entire source file except file inclusion, declaration/definition of global identifiers, and forward declaration of the class to distinguish it from other namespaces.
// .h file
namespace mynamespace { // All declarations are placed in the namespace // Be careful not to use indentation class MyClass { public: void Foo(); }; } // namespace mynamespace // .cc file namespace mynamespace { // Function definitions are placed in the namespace void MyClass::Foo() { } } // namespace mynamespace Usually .cc files will contain more and more complex details, including references to classes in other namespaces wait. #include “ah” DEFINE_bool(someflag, false, “dummy flag”); class C; // Predeclaration of class C in the global namespace namespace a { class A; } // Class a in namespace a:: A’s forward declaration namespace b { …code for b… // code in b






















} // namespace b
Do not declare anything under the namespace std, including forward declarations of standard library classes. Declaring entities under std can lead to unclear behavior, e.g., not portable. To declare an entity under the standard library, you need to include the corresponding header file.
It is best not to use the using directive to ensure that all names under the namespace can be used normally.
// Prohibited - polluting the namespace
using namespace foo;
using can be used in functions, methods or classes in .cc files, .h files.
// Allowed: in .cc files // In .h files, using ::foo::bar
must be used inside functions, methods or classes ; in .cc files, .h files, functions, methods or classes, also Namespace aliases can be used. // Allowed: in .cc files // In .h files, must be used inside functions, methods or classes



namespace fbz = ::foo::bar::baz;

2.2. Nested Class

When exposing nested classes as part of an interface, it is better to place the declaration of the nested class in a namespace, although it is possible to keep them in the global scope directly.
Definition: You can define another class within a class. Nested classes are also called member classes.
class Foo { private: // Bar is a member class nested in Foo class Bar { ... }; }; Advantages: Useful when nested (member) classes are only used in the enclosing class, Placing it in the nested class scope as a member of the nested class will not pollute other scopes with the same class. You can pre-declare the nested class in the nested class and define the nested class in the .cc file to avoid including the definition of the nested class in the nested class, because the definition of the nested class is usually only relevant to the implementation. Disadvantage: Nested classes can only be forward-declared in the definition of the nested class. Therefore, any header file that uses Foo::Bar* pointers must contain the entire declaration of Foo. Conclusion: Don't define nested classes as public unless they are part of an interface, for example, a method uses a set of options from the class.








2.3.Nonmember, Static Member and Global Functions

Use non-member functions or static member functions in the namespace, and try not to use global functions.
Advantages: In some cases, non-member functions and static member functions are very useful. Placing non-member functions in the namespace can avoid pollution of the global scope.
Disadvantages: It may make more sense to make non-member functions and static member functions members of a new class, especially when they need to access external resources or have important dependencies.
Conclusion:
Sometimes it is beneficial, or even necessary, to not confine a function to the entity of the class, either as a static member or as a non-member function. Non-member functions should not depend on external variables and should be placed in a namespace as much as possible. Rather than creating a class simply to encapsulate a number of static member functions that do not share any static data, it is better to use a namespace.
Functions defined in the same compilation unit that are directly called by other compilation units may introduce unnecessary coupling and connection dependencies; static member functions are particularly sensitive to this. Consider extracting into a new class, or placing the function in a separate library's namespace.
If you really need to define a non-member function and only use it in a .cc file, you can use an unnamed namespace or static association (such as static int Foo() {…}) to limit its scope.

2.4.Local Variables

Place function variables in the smallest scope possible and initialize them when declaring them.
C++ allows variables to be declared anywhere in a function. We advocate declaring variables in the smallest scope possible, as close to first use as possible. This makes the code easier to read, making it easier to locate the variable's declaration location, variable type, and initial value. In particular, initialization should be used instead of declaration+assignment.
int i;
i = f(); // Bad - separation of initialization and declaration
int j = g(); // Good - declaration during initialization Note
: gcc can correctly execute for (int i = 0; i < 10; ++i) (the scope of i is limited to for loops), so i can be reused in other for loops. In if and while statements, scope declarations are also correct.
while (const char* p = strchr(str, '/')) str = p + 1;
Note: If the variable is an object, its constructor must be called every time it enters the scope, and its constructor must be called every time it exits the scope. its destructor.
// Inefficient implementation
for (int i = 0; i < 1000000; ++i) { Foo f; // The constructor and destructor are called 1000000 times each! f.DoSomething(i); } It is much more efficient to declare similar variables outside the loop scope: Foo f; // The constructor and destructor are only called once for (int i = 0; i < 1000000; ++ i) { f.DoSomething(i); }







2.5.Global Variables

Global variables of class type are prohibited, but global variables of built-in types are allowed. Of course, non-constant global variables in multi-threaded code are also prohibited. Never use function return values ​​to initialize global variables.
Unfortunately, the order in which global variables' constructors, destructors, and initialization operations are called is only partially specified and may change each build, leading to hard-to-find bugs.
Therefore, it is prohibited to use class type global variables (including STL string, vector, etc.) because their initialization order may cause construction problems. Built-in types and structures without constructors composed of built-in types can be used. If you must use global variables of class type, please use the singleton pattern.
For global string constants, use C-style strings instead of STL strings:
const char kFrogSays[] = "ribbet";
Although global variables are allowed to be used in the global scope, be sure to think twice before using them. Most global variables should be static data members of the class, either defined into an unnamed namespace when used only within a .cc file, or use static associations to limit the scope of the variable.
Remember, static member variables are considered global variables, so they cannot be of class type!

2.6.Summary

  1. The unnamed namespace in .cc can avoid naming conflicts, limit scope, and avoid directly using the using prompt to pollute the namespace;
  2. Nested classes comply with the principle of local use, but they cannot be forward-declared in other header files, and try not to be public;
  3. Try not to use global functions and global variables, consider scope and namespace restrictions, and try to form separate compilation units;
  4. Do not use class types (including STL containers) for global variables (including static member variables) in multi-threads to avoid bugs caused by unclear behavior.
    In addition to considering name pollution and readability, the use of scope is mainly to reduce coupling and improve compilation and execution efficiency.

3. Class

Class is the basic code unit in C++ and is naturally widely used. This section lists the dos and don'ts when writing a class.

3.1. Responsibilities of Constructor

Only trivial initializations are carried out in the constructor, if possible, Use the Init() method to centrally initialize to meaningful (non-trivial) data.
Definition: Perform initialization operations in the constructor.
Advantages: Convenient typesetting, no need to worry about whether the class is initialized.
Disadvantages: Problems caused by performing operations in the constructor include:

  1. Errors are not easily reported in constructors and exceptions cannot be used.
  2. Failure of the operation will cause the object to fail to initialize, causing an uncertain state.
  3. If a virtual function is called within the constructor, the call will not be dispatched to the subclass implementation. Even if there is no subclass implementation currently, it will still be a hidden danger in the future.
  4. If someone created a global variable of this type (albeit against the rules mentioned in the previous section), the constructor would be called before main(), potentially breaking the assumptions implicit in the constructor. For example, gflags has not been initialized yet.
    Conclusion: If the object requires meaningful (non-trivial) initialization, consider using an additional Init() method and/or adding a member flag to indicate whether the object has been successfully initialized.

3.2.Default Constructors

If a class defines several member variables and has no other constructors, it needs to define a default constructor, otherwise the compiler will automatically generate a default constructor.
Definition: When creating an object without parameters, the default constructor is called. When new[] (for an array) is called, the default constructor is always called.
Advantages: Structures are initialized to "impossible" values ​​by default, making debugging easier.
Disadvantage: This is redundant work for the code writer.
Conclusion:
If the class has member variables defined and no other constructors are provided, you need to define a default constructor (without parameters). The default constructor is more suitable for initializing the object and making the internal state of the object consistent and valid.
The reason for providing a default constructor is: if you do not provide other constructors and do not define a default constructor, the compiler will automatically generate one for you. The constructor generated by the compiler will not initialize the object.
If the class you define inherits an existing class and you do not add new member variables, you do not need to define a default constructor for the new class.

3.3. Explicit Constructors

Use the C++ keyword explicit for single-argument constructors.
Definition: Usually, a constructor with only one parameter can be used for conversion (translator's note: mainly refers to implicit conversion, see below). For example, Foo::Foo(string name) is defined, and when it needs to be passed in When a function on a Foo object is passed in a string, the constructor Foo::Foo(string name) is called and converts the string into a Foo temporary object and passes it to the calling function. It seems convenient, but if you don't want to generate a new object through conversion, trouble will arise. To avoid implicit conversion caused by the constructor being called, you can declare it as explicit.
Advantages: Avoid untimely changes.
Disadvantages: None.
Conclusion:
All single-argument constructors must be explicit. In the class definition, add the keyword explicit before the single-argument constructor: explicit Foo(string name);
Exception: In a few cases, the copy constructor does not need to be declared explicit; a class that is intentionally used as a transparent wrapper for other classes . Such exceptions should be clearly stated in the comments.

3.4.Copy Constructors

Use the copy constructor only when you need to copy a class object in your code; use DISALLOW_COPY_AND_ASSIGN when copying is not required.
Definition: The copy constructor can be used when creating a new object by copying (especially when passing an object by value).
Advantages: The copy constructor makes it easier to copy objects. STL containers require that all contents be copyable and assignable.
Disadvantages: Implicit copies of objects in C++ are the source of many performance problems and bugs. The copy constructor reduces code readability. Compared with passing by reference, it is more difficult to track objects passed by value, and the place where the object is modified becomes elusive.
Conclusion:
A large number of classes do not need to be copyable, nor do they need a copy constructor or assignment operator. Unfortunately, if you don't declare them actively, the compiler will automatically generate them for you and make them public.
You can consider adding a dummy copy constructor and assignment operation to the private part of the class, with only declarations and no definitions. Because these empty programs are declared private, the compiler will report an error when other code attempts to use them. For convenience, you can use the macro DISALLOW_COPY_AND_ASSIGN:
// Macros that prohibit the use of copy constructors and assignment operations
// should be used in the private: of the class
#define DISALLOW_COPY_AND_ASSIGN(TypeName)
TypeName(const TypeName&);
void operator=(const TypeName&)
class Foo { public: Foo(int f); ~Foo(); private:




DISALLOW_COPY_AND_ASSIGN(Foo);
};
As mentioned above, DISALLOW_COPY_AND_ASSIGN should be used in most cases. If the class really needs to be copyable, the reason should be explained in the header file of the class, and the copy constructor and assignment operation should be appropriately defined. Note Detect self-assignment situations in operator=.
When using a class as an STL container, you may be tempted to make the class copyable. In similar situations, what you should really do is use a pointer to point to the object in the STL container. You can consider using std::tr1::shared_ptr.

3.5. Structures and classes (Structs vs. Classes)

Use struct only when there is only data, and use class for everything else.
In C++, the keywords struct and class have almost the same meaning. We add artificial semantics to them so that we can reasonably choose which keyword to use for the defined data type.
Struct is used on passive objects that only contain data, which may include associated constants, but have no function functions other than accessing data members. The access function is implemented through direct access without method calls. Here we provide The mentioned methods are those used only for processing data members, such as constructors, destructors, Initialize(), Reset(), and Validate().
If you need more functions, class is more suitable. If you are not sure, use class directly.
If combined with STL, you can use struct instead of class for functors and traits.
Note: Member variables of classes and structures use different naming rules.

3.6. Inheritance

Using composition (composition, translator's note, this is also emphasized repeatedly by GoF in "Design Patterns") is usually more appropriate than using inheritance. If inheritance is used, only use public inheritance.
Definition: When a subclass inherits a base class, the subclass contains the definitions of all data and operations of the parent base class. In C++ practice, inheritance is mainly used in two situations: implementation inheritance, where the subclass inherits the implementation code of the parent class; interface inheritance, where the subclass only inherits the method names of the parent class.
Advantages: Implementing inheritance reduces the amount of code by reusing the base class code intact. Because inheritance is a compile-time declaration, both coders and compilers can understand the operations and catch errors. Interface inheritance can be used to programmatically enhance the functionality of a specific API of a class. The compiler can also detect errors when the class does not define the necessary implementation of the API.
Disadvantages: For implementation inheritance, it becomes more difficult to understand the implementation because the code to implement the subclass is extended between the parent class and the subclass. Subclasses cannot override non-virtual functions of the parent class, and of course they cannot modify their implementation. The base class may also define some data members, and the physical layout of the base class must also be distinguished.
Conclusion:
All inheritance must be public. If you want private inheritance, you should include a base class instance as a member instead.
Don't use implementation inheritance too much; composition is usually more appropriate. Try to use inheritance only if it "is a" ("is-a", translator's note, please use combination for other "has-a" cases): if Bar is indeed "a" kind of Foo, then let Bar is a subclass of Foo.
If necessary, make the destructor virtual. Necessity means that if the class has a virtual function, its destructor should be a virtual function.
Translator's note: As for the special case where the subclass does not have additional data members, or even the parent class does not have any data members, whether the call to the destructor is necessary is a semantic debate. From the perspective of programming design specifications, in the parent class containing virtual functions In a class, it is absolutely necessary to define a virtual destructor.
Member functions that are restricted to subclasses are protected. It should be noted that data members should always be private.
When redefining a derived virtual function, explicitly declare it as virtual in the derived class. Root cause: If virtual is omitted, the reader needs to retrieve all ancestors of the class to determine whether the function is a virtual function (Translator's Note, although it does not affect the nature of it being a virtual function).

3.7.Multiple Inheritance

There are very few times when multiple implementation inheritance is really needed. Multiple inheritance is used only when at most one base class contains an implementation and the other base classes are pure interface classes with the suffix Interface.
Definition: Multiple inheritance allows subclasses to have multiple base classes. It is necessary to distinguish between base classes that are pure interfaces and base classes that have implementations.
Advantages: Multiple implementation inheritance allows you to reuse more code than single inheritance.
Disadvantages: There are very few times when multiple implementation inheritance is really needed. Multiple implementation inheritance looks like a good solution, and you can usually find a more clear, clear, and different solution.
Conclusion: Multiple inheritance can be used only if all superclasses except the first one are pure interfaces. To ensure that they are pure interfaces, these classes must be suffixed with Interface.
Note: Regarding this rule, there is an exception under Windows (Translator's Note, will be explained in the rule exceptions in the last article of this translation).

3.8.Interface

Interfaces refer to classes that meet specific conditions. These classes are suffixed with Interface (not required).
Definition: When a class meets the following requirements, it is called a pure interface:

  1. Only pure virtual functions ("=0") and static functions (except destructors mentioned below);
  2. There are no non-static data members;
  3. No constructor is defined. If there is, it does not contain parameters and is protected;
  4. If it is a subclass, it can only inherit classes that meet the above conditions and have Interface as the suffix.
    The interface class cannot be instantiated directly because it declares pure virtual functions. To ensure that all implementations of an interface class can be destroyed correctly, a virtual destructor must be declared for it (as an exception to rule 1, the destructor cannot be a pure virtual function). For specific details, please refer to Section 12.4 of Stroustrup's "The C++ Programming Language, 3rd edition".
    Advantages: Using the Interface suffix allows others to know that they cannot add implementation functions or non-static data members to the interface class. This is especially important for multiple inheritance. In addition, for Java programmers, the concept of interface has been deeply rooted in the hearts of the people.
    Disadvantages: The Interface suffix increases the length of the class name, which brings inconvenience to reading and understanding. At the same time, the interface characteristics should not be exposed to customers as implementation details.
    in conclusion:. Only when the above needs are met, the class ends with Interface, but conversely, the class that meets the above needs does not necessarily end with Interface.

3.9. Operator Overloading

Except in a few specific circumstances, do not overload operators.
Definition: A class can define operators such as +, /, etc., so that they can be used directly like built-in types.
Advantages: Make the code look more intuitive, just like built-in types (such as int), overloaded operators make those dull function names such as Equals(), Add(), etc. much more interesting. In order for some template functions to work correctly, you may need to define operators.
Disadvantages: Although operator overloading makes the code more intuitive, it also has some shortcomings

  1. Confusing your intuition, making you think that some time-consuming operations are as lightweight as built-in operations;
  2. It is more difficult to find the call site of the overloaded operator. Finding Equals() is obviously much easier than the equivalent call ==;
  3. Some operators can operate on pointers, which can easily lead to bugs. Foo + 4 does one thing, while &Foo + 4 may do another completely different thing. For both, the compiler will not report an error. , making it difficult to debug;
  4. Overloading also has side effects that may surprise you. For example, classes that overload operator & cannot be forward-declared.
    Conclusion:
    Generally do not overload operators, especially the assignment operation (operator=) is more insidious, so overloading should be avoided. If necessary, you can define functions like Equals(), CopyFrom(), etc.
    However, there are rare cases where it is necessary to overload operators in order to interface with templates or "standard" C++ classes (such as operator<<(ostream&, const T&)). This is acceptable if justified, but you should avoid it if possible. this way. In particular, don't overload the operator just to use it as a key in the STL containeror operator<, instead you should create equality and size comparison functor types when declaring the container.
    Some STL algorithms do require operator overloading
    You can do this when doing so, and don’t forget to document why.
    See copy constructor and function overloading.

3.10.Access Control

Privatize data members and provide related access functions, such as defining variable foo_ and value function foo(), and assignment function set_foo().
The definition of access functions is usually inline in the header file.
See inheritance and function naming.

3.11.Declaration Order

Use a specific declaration order in a class: public: before private:, member functions before data members (variables).
The order of definition is as follows: public:, protected:, private:. If that part is not available, just ignore it.
Within each block, the declaration order is generally as follows:

  1. typedefs和enums;
  2. constant;
  3. Constructor;
  4. destructor;
  5. Member functions, including static member functions;
  6. Data members, including static data members.
    The macro DISALLOW_COPY_AND_ASSIGN is placed after the private: block as the last part of the class. See copy constructor.
    The definition of functions in the .cc file should be as consistent as possible with the declaration order.
    Do not inline large functions into the class definition. Usually, only short functions that have no special meaning or high performance requirements are defined as inline functions. For more details, refer to the inline functions in the first article of the translation.

3.12. Write Short Functions

Prefer short, concise functions.
Long functions are sometimes appropriate, so there is no strict limit on function length. If the function exceeds 40 lines, consider dividing it without affecting the program structure.
Even if a long function works very well now, once someone modifies it, new problems may arise, or even lead to hard-to-find bugs. Keep functions as short and simple as possible to make it easier for others to read and modify the code.
As you work on your code, you may find long, complex functions. Don’t be afraid to modify existing code: if it proves difficult to use, debug, or you need to use a small piece of it, consider splitting it into smaller, more manageable pieces. several functions.

3.13.Summary

  1. Don’t do too many logically related initializations in the constructor;
  2. The default constructor provided by the compiler does not initialize variables. If other constructors are defined, the compiler will no longer provide them, and the coder needs to provide a default constructor by himself;
  3. To avoid implicit conversion, the single-parameter constructor needs to be declared explicit;
  4. In order to avoid the abuse of copy constructors and assignment operations and automatic generation by the compiler, you can currently declare them as private and do not need to be implemented;
  5. Use struct only as a data collection;
  6. Composition > Implementation inheritance > Interface inheritance > Private inheritance, virtual functions overloaded by subclasses must also declare the virtual keyword, although the compiler does not allow this;
  7. Avoid using multiple inheritance. When using it, except for one base class that contains implementation, the other base classes are pure interfaces;
  8. The interface class name is suffixed with Interface. Except for providing implemented virtual destructors and static member functions, the others are pure virtual functions. It does not define non-static data members and does not provide constructors. If provided, declare it as protected;
  9. To reduce complexity, try not to overload operators, and provide documentation when used in templates and standard classes;
  10. Access functions are generally inlined in header files;
  11. Declaration order: public->protected->private;
  12. The function body should be as short and compact as possible, with a single function.

4. Experience from Google

Google has many self-implemented techniques and functions that make C++ code more robust, as well as ways of using C++ that are different from elsewhere.

4.1.Smart Pointers

If you really need to use smart pointers, scoped_ptr is fully capable. In very special cases, such as for objects in STL containers, you should only use std::tr1::shared_ptr, and never use auto_ptr under any circumstances.
"Smart" pointers look like pointers, but are actually objects with added semantics. Taking scoped_ptr as an example, when scoped_ptr is destroyed, the object it points to is deleted. The same is true for shared_ptr, and shared_ptr implements reference-counting so that the pointer will only be deleted when the last object it points to is destroyed.
Generally speaking, we tend to design code with clear object affiliation. The clearest object affiliation is to use the object directly as a field or local variable without using pointers at all. The other extreme is that the reference-counted pointer does not belong to any object. The problem with this design is that it can easily lead to circular references or other weird conditions that prevent the object from being deleted, and even atomic operations will be very slow during each copy or assignment.
Although not recommended, there are times when a reference-counted pointer is the simplest and most effective solution.

4.2.CPPlint

Use cpplint.py to detect style errors.
Cpplint.py is a tool that reads source files and identifies style errors. Although not perfect and has many advantages and disadvantages, it is still a useful tool. Active error messages can be ignored by placing //NOLINT at the end of the line.
Some projects come with instructions on how to run cpplint.py from the project tools. If not, you can download it separately.
Translator's Note: It seems that what Google calls the difference is to try to avoid using smart pointers :D, try to be localized when using them, and put safety first.

5. Other C++ features

5.1. Reference Arguments

So parameters passed by reference must be added with const.
Definition: In C language, if a function needs to modify the value of a variable, the formal parameter must be a pointer, such as int foo(int *pval). In C++, functions can also declare reference parameters: int foo(int &val).
Advantages: Defining formal parameters as references avoids ugly code like (*pval)++. It is also necessary for applications such as copy constructors, and unlike pointers, it does not accept null pointers NULL.
Disadvantages: It is easy to cause misunderstanding, because the reference is syntactically a value but has the semantics of a pointer.
Conclusion:
In the function parameter list, all references must be const:
void Foo(const string &in, string out);
In fact, this is a hard convention: the input parameters are values ​​or constant references, and the output parameters are pointers; the input parameters can be Constant pointers, but non-const reference parameters cannot be used.
When emphasizing that parameters are not copied and must exist throughout the object's lifetime, constant pointers can be used. It is best to specify these in comments. STL adapters such as bind2nd and mem_fun do not accept reference parameters, in which case the function must also be declared with pointer parameters.
5.2. Function Overloading
Only use overloaded functions (including constructors) when the input parameter types are different and the functions are the same. Do not use function overloading to imitate default function parameters.
Definition: You can define a function parameter type as const string&, and define its overloaded function type as const char
.
class MyClass { public: void Analyze(const string &text);


void Analyze(const char *text, size_t textlen);
};
Advantages: By overloading functions of the same name with different parameters, the code is more intuitive, templated code needs to be overloaded, and it brings convenience to visitors.
Disadvantages: One reason for limiting the use of overloading is that it is difficult to determine which function is called at a specific call. Another reason is that when a derived class only overloads some variables of the function, many people will be confused about inheritance semantics. In addition, when reading the client code of the library, the default function parameters cause unnecessary confusion.
Conclusion: If you want to overload a function, consider letting the function name contain parameter information, for example, use AppendString(), AppendInt() instead of Append().

5.3.Default Arguments

Disable functions from using default parameters.
Advantages: A function is often used with a large number of default values, and these values ​​are occasionally overridden. Default parameters provide the convenience of defining fewer functions for rarely involved exceptions.
Disadvantages: People often look at existing code to determine how to use an API. Default parameters make it difficult to copy and paste previous code to present all parameters. It can cause major problems when default parameters do not apply to new code.
Conclusion: All parameters must be clearly specified, forcing programmers to consider the API and the values ​​of each parameter passed in, and avoid using default parameters that may not be known to programmers.

5.4. Variable-Length Arrays and alloca()

Variable-length arrays and alloca() are prohibited.
Advantages: Variable-length arrays have a natural syntax, and variable-length arrays and alloca() are also very efficient.
Disadvantages: Variable-length arrays and alloca() are not part of standard C++, and more importantly, they allocate sizes based on data on the stack (stack) which can lead to hard-to-find memory leaks: "It runs fine on my machine , but it died inexplicably when it arrived in the product.”
Conclusion: Use a safe allocator such as scoped_ptr/scoped_array.

5.5.Friends

Reasonable use of friend classes and friend functions is allowed.
Friends are usually defined in the same file to prevent readers from going to other files to find their use of private members of a class. One place where friends are often used is to declare FooBuilder as a friend of Foo, so that FooBuilder can correctly construct Foo's internal state without exposing that state. In some cases, it is convenient to declare a unit test class as a friend of the class under test.
Friends extend (but do not break) the encapsulation boundaries of a class. When you want to allow only another class to access a member, it is usually better to use friends than to declare it as public. Of course, most classes should only provide public members with which to interact.

5.6.Exceptions

Don't use C++ exceptions.
advantage:

  1. Exceptions allow upper-level applications to decide how to handle "impossible" failures that occur in underlying nested functions, unlike records of error codes that are as obscure and convoluted as possible;
  2. Used in many other modern languages, the introduction of exceptions makes C++ more compatible with Python, Java and other languages ​​similar to C++;
  3. Many C++ third-party libraries use exceptions, and turning them off will make it difficult to combine them;
  4. Exceptions are the only solution to constructor failure. Although exceptions can be simulated through the factory function or the Init() method, they require heap allocation or a new "illegal" state respectively;
  5. Exceptions are really useful in a testing framework.
    shortcoming:
  6. When adding a throw statement to an existing function, all call sites must be checked, and even if they have at least basic exception safety protection, or the program ends normally, the exception can never be caught. For example: if f() calls g() calls h(), h throws an exception caught by f, g should be careful to avoid incomplete cleanup;
  7. In layman's terms, exceptions will cause the program control flow (control flow) to be unable to be determined by looking at the code: the function may return at an uncertain place, making code management and debugging difficult. Of course, you can specify when, where and how to use it. Exceptions are used to minimize overhead, but put the burden on developers to master these regulations;
  8. Exception safety requires RAII and different coding practices. Writing exception-safe code easily and correctly requires a lot of support. Allow exceptions;
  9. Adding exceptions will increase the size of the binary execution code, increase the compilation time (may not have much impact), and may also increase the pressure on the address space;
  10. The usefulness of exceptions may incentivize developers to throw exceptions at inappropriate times or to recover from exceptions in unsafe places, for example, where illegal user input may cause an exception to be thrown. Allowing exceptions would make a programming style guide like this much longer.
    Conclusion:
    On the surface, the advantages of using exceptions outweigh the disadvantages, especially in new projects. However, for existing code, introducing exceptions will implicate all dependent code. If exceptions are allowed to be used in new projects, it will also be troublesome when integrating with code that did not use exceptions before. Because most of Google's existing C++ code does not have exception handling, introducing new code with exception handling is quite difficult.
    Since Google's existing code does not accept exceptions, using exceptions in existing code is more expensive than using them in new projects. The migration process will be slower and error-prone. We also don't believe that valid alternatives to exceptions, such as error codes, assertions, etc., are seriously burdensome.
    We do not object to the use of exceptions on philosophical or moral grounds, but on practical grounds. Because we hope to use open source projects on Google, but using exceptions in the project will cause inconvenience, because we also recommend not to use exceptions in open source projects on Google. It is obviously unrealistic if we need to tear down these projects and start over. .
    There is an exception to this for Windows code (wait until the last post :D).
    Translator's Note: Exception handling is obviously not something that can be explained clearly in just a few sentences. Take the constructor as an example. Many C++ books mention that only exceptions can be handled when the construction fails. Google prohibits the use of exceptions. It's for your own convenience. To put it bluntly, it's just based on the cost of software management. In actual use, you still make your own decision.

5.7. Run-Time Type Information (RTTI)

We prohibit the use of RTTI.
Definition: RTTI allows programmers to identify the type of C++ class objects at runtime.
Advantages: RTTI is very useful in certain unit tests, such as when testing factory classes to check whether a newly created object is the expected dynamic type.
Rarely used except for testing.
Disadvantages: Identifying types at runtime means there is a problem with the design itself. If you need to determine the type of an object at runtime, this usually means that you need to rethink the design of your class.
Conclusion: Do not use RTTI except for unit testing. If you find that the code you need to write behaves differently depending on the object type, consider another way to identify the object type.
Virtual functions can execute different codes depending on the type of subclass, and the work is left to the object itself.
If the work is done in code outside the object, consider a dual dispatch scheme, such as the Visitor pattern, which makes it easy to determine the type of the class outside the object itself.
If you think you can't master the above method, you can use RTTI, but please think twice and don't manually implement a solution that looks like RTTI (RTTI-like workaround). We are opposed to the use of RTTI, and we are also opposed to the seemingly class inheritance with type labels. alternatives.

5.8. Type conversion (Casting)

Use C++ type conversions such as static_cast<>(). Do not use int y = (int)x or int y = int(x).
Definition: C++ introduces different types of type conversion operations that are different from C.
Advantages: The problem with type conversion in C language is that the operation of Shandong Haihua is relatively ambiguous: sometimes it is doing forced conversion (such as (int)3.5), sometimes it is doing type conversion (such as (int) "hello" "). In addition, C++'s type conversion lookup is easier and more eye-catching.
Disadvantages: The syntax is nasty.
Conclusion: Use C++ style rather than C style casts.

  1. static_cast: Similar to C-style conversion, it can do value coercion, or explicit up-conversion of a pointer from a parent class to a subclass;
  2. const_cast: remove const attributes;
  3. reinterpret_cast: unsafe conversion between pointer types and integer or other pointers, only use it when you know everything you are doing;
  4. dynamic_cast: Do not use it except for testing. Except for unit testing, if you need to determine type information at runtime, the design is defective (see RTTI).

5.9. Streams

Only use streams when logging.
Definition: Streams are a replacement for printf() and scanf().
Advantages: With streams, you don't need to care about the type of the object when outputting, and you don't have to worry about the format string not matching the parameter list (although this problem does not exist when using printf in gcc). When opening and closing the corresponding file, the stream can Automatic construction and destruction.
Disadvantages: Streams make it difficult to execute functional functions such as pread(). If you do not use functions such as printf but use streams, it is difficult to operate the format (especially the commonly used format string %.*s). Streams do not support characters. String operator reordering (%1s), which is useful for internationalization.
Conclusion: Do not use streams unless required by the logging interface, use printf or the like instead. There are many pros and cons of using streams, code consistency trumps everything, don't use streams in your code.
Extended discussion:
There is some debate about this rule, and here are the deeper reasons why. Recall the principle of uniqueness (Only One Way): We want to use only one certain I/O type at any time to make the code consistent at all I/O. Therefore, we don't want the user to decide whether to use streams or printf + read/write, we should decide which method to use. The reason for making logs an exception is that streams are well suited for this, and there are historical reasons for this.
Proponents of streaming argue that streaming is the obvious choice, but their argument is not clear and strong. All the advantages they point out are also its disadvantages. The biggest advantage of streams is that you don’t need to care about the type of the output object when outputting. This is a highlight, but also a shortcoming: it is easy to use the wrong type, and the compiler will not alert you. One type of error that is easily caused when using streams is:
cout << this; // Prints the address
cout << *this; // Prints the contents.
The compiler will not report an error because << is overloaded. Because of this we The use of operator overloading is discouraged.
Some people say printf's formatting is ugly and unreadable, but streams aren't much better. Take a look at the following two pieces of code. Which one is more readable?
cerr << “Error connecting to '" << foo->bar()->hostname.first
<< “:” << foo->bar()->hostname.second << ": " << strerror(errno );

fprintf(stderr, “Error connecting to '%s:%u: %s”,
foo->bar()->hostname.first, foo->bar()->hostname.second,
strerror(errno));
you You might say, "It would be better to encapsulate the stream." That's OK here, but what about other places? And don't forget, the goal is to make the language as small as possible, not to add some new stuff that others need to learn.
Each method has its own pros and cons. "There is no best, only better." The simplistic dogma warns us that we must choose one of them. The final majority decision is printf + read/write.

5.10.Preincrement and Predecrement

Use the prefixed form (++i) of the increment and decrement operators for iterators and other template objects.
Definition: When the value of the expression is not used after the variable is incremented (++i or i++) or decremented (–i or i–), it is necessary to determine whether to use prefixed or postfixed auto. Increase and decrease.
Advantages: Regardless of the return value, pre-increment (++i) is usually more efficient than post-increment (–i), because post-increment and decrement require a copy of the value i of the expression. , if i is an iterator or other non-numeric type, the cost of copying is relatively high. Since the two auto-increment methods have the same action, why not just use the pre-increment directly?
Disadvantages: In C language, when the value of an expression is not used, the traditional approach is to use post-increment, especially in for loops. Some people think post-increment is easier to understand because it is like natural language. The subject (i) comes before the predicate verb (++).
Conclusion: For simple values ​​(non-objects), it doesn't matter either way. For iterators and template types, use prefixed increment (decrement).

5.11.Use of const

We strongly recommend that you use const wherever possible.
Definition: Add the keyword const before the declared variable or parameter to indicate that the variable value cannot be modified (such as const int foo). Add the const qualification to the function in the class to indicate that the function will not modify the state of the class member variable (such as class Foo { int Bar(char c) const; };).
Pros: It's easier for people to understand how variables are used, and editors can do better type checking and better code generation. People feel more confident about writing correct code because they know that the functions they call are restricted in that they can or cannot modify variable values. Even in lock-free multithreaded programming, people know what functions are safe.
Disadvantages: If you pass a const variable to a function, the function prototype must also be const (otherwise the variable needs const_cast type conversion), which is especially troublesome when calling library functions.
Conclusion: const variables, data members, functions and parameters add a layer of protection to compile-time type detection and better detect errors as early as possible. Therefore, we strongly recommend using const wherever possible:

  1. If the function does not modify the passed reference or pointer type parameters, such parameters should be const;
  2. Declare functions as const whenever possible. Access functions should always be const. Other functions that do not modify any data members should also be const. Do not call non-const functions and do not return non-const pointers or references to data members;
  3. If a data member does not change after the object is constructed, it can be defined as const.
    However, don't overuse const. For example, const int * const * const x; is a bit excessive. Even if it accurately describes x, you can actually write it as const int** x.
    The keyword mutable can be used, but it is unsafe in multi-threading. When using it, you must first consider thread safety.
    const position:
    Some people prefer the form of int const foo to const int foo. They think that the former is more consistent and therefore more readable: it follows the principle that const is always located after the object it describes (int). However, the principle of consistency does not apply here, and the authority of "don't overuse" counteracts consistent use. Putting const first is easier to read, because in natural language the adjective (const) comes before the noun (int).
    This means that we advocate putting const first, not a requirement, but we must take into account the consistency of the code!

5.12.Integer Types

Among the built-in integer types in C++, the only one used is int. If variables of different sizes are needed in the program, you can use the precise-width integer type in <stdint.h>, such as int16_t.
Definition: C++ does not specify the size of integer types. Generally, people think that short is 16 bits, int is 32 bits, long is 32 bits, and long long is 64 bits.
Advantages: Keeps statements consistent.
Disadvantages: Integer sizes in C++ vary between compilers and architectures.
Conclusion:
<stdint.h> defines int16_t, uint32_t, int64_t and other integer types. When you need to determine the size of the integer type, you can use them instead of short, unsigned long long, etc. Among the C integer types, only int is used. Where appropriate, it is recommended to use standard types such as size_t and ptrdiff_t.
The most commonly used one is that for integers, which are usually not too large, such as loop counting, ordinary int can be used. You can think of int as at least 32 bits, but don't think it will be more than 32 bits. If you need a 64-bit integer, you can use int64_t or uint64_t.
For large integers, use int64_t.
Don't use unsigned integers such as uint32_t unless you are representing a bit pattern rather than a value. Do not use unsigned types even if the value will not be negative, use assertions to protect the data.
Unsigned integer types:
Some people, including some textbook authors, recommend using unsigned types to represent nonnegative numbers, where the type indicates the form of the numerical value. However, in C, this advantage is outweighed by the bugs it causes. Take a look:
for (unsigned int i = foo.Length()-1; i >= 0; --i) …
The above code never terminates! Sometimes gcc will find the bug and alert you, but usually not. Similar bugs will also appear when comparing qualified variables and unsigned variables, mainly because C's type promotion mechanism (type-promotion scheme, the promotion conversion relationship between various built-in types in the C language) will cause unsigned types to The behavior is unexpected.
Therefore, use assertions to declare variables to be non-negative and do not use unsigned types.

5.13.64-bit Portability

In principle, the code should be more friendly in both 64-bit and 32-bit systems, especially for output, comparison, and structure alignment:

  1. Some types specified by printf() are not very portable on 32-bit and 64-bit systems, and the C99 standard defines some portable formats. Unfortunately, MSVC 7.1 doesn't support them all, and there are some missing from the standard. So sometimes we have to define the ugly version ourselves (using the standard style includes the file inttypes.h):
    // printf macros for size_t, in the style of inttypes.h
    #ifdef _LP64
    #define __PRIS_PREFIX "z"
    #else
    #define __PRIS_PREFIX
    #endif

// Use these macros after a % in a printf format string
// to get correct 32/64 bit behavior, like this:
// size_t size = records.size();
// printf(“%“PRIuS”\n”, size);

#define PRIdS __PRIS_PREFIX "d"
#define PRIxS __PRIS_PREFIX "x"
#define PRIuS __PRIS_PREFIX "u"
#define PRIXS __PRIS_PREFIX "X"
#define PRIoS PRIS_PREFIX "o"
type do not use use remarks
void (or other pointer type) %lx % p
int64_t %qd, %lld %"PRId64"
uint64_t %qu, %llu, %llx %"PRIu64", %"PRIx64"
size_t %u %"PRIuS", %"PRIxS" C99 specification %zu
ptrdiff_t %d %" PRIdS" C99 specifies %zd.
Note that the macro PRI
will be expanded into an independent string by the compiler. Therefore, if you use a non-const format string, you need to insert the value of the macro into the format instead of the macro name. The same is true when using the macro PRI* Length and other information can be specified after %. For example, printf("x = %30"PRIuS"\n", x) will be expanded to printf("x = %30" "u" "\n", x) on 32-bit Linux, and the compiler will handle is printf(“x = %30u\n”, x).
2) Remember sizeof(void *) != sizeof(int), if you need a pointer-sized integer, use intptr_t.
3) Care needs to be taken with structure alignment, especially for structures stored on disk. On 64-bit systems, any class/structure with int64_t/uint64_t members will be processed as 8-byte alignment by default. If 32-bit and 64-bit code share structures on disk, you need to ensure that the structures are aligned consistently on both architectures. Most compilers provide a way to adjust the alignment of structures. __attribute ((packed)) can be used in gcc
, and MSVC provides #pragma pack() and __declspec(align()).
4) Use LL or ULL as suffix when creating 64-bit constants, such as:
int64_t my_value = 0x123456789LL;
uint64_t my_mask = 3ULL << 48;
5) If you really need different codes for 32-bit and 64-bit systems, you can precede the code variable use. (Try not to do this, and try to localize the modifications when using it).

5.14. Preprocessor Macros

Be careful when using macros and try to use inline functions, enumerations, and constants instead.
Macros mean that you and the compiler see the code differently, which can lead to unexpected behavior, especially if the macro exists in the global scope.
Fortunately, in C++, macros are not as necessary as in C. Macro inlining efficiency-critical code (performance-critical code) can be replaced by inline functions; macro stored constants can be replaced by const variables; macros "abbreviate" long variable names can be replaced by references; use macros for conditional compilation, this..., it is best not to Doing so will make testing more painful (with the exception of #define to prevent header files from being re-included).
Macros can do some things that cannot be achieved by other technologies. In some code libraries (especially in low-level libraries), you can see certain features of macros (such as stringifying (Translator's Note, use #), concatenation, Translator's note, use ##), etc.). But before using it, carefully consider whether you can achieve the same effect without using macros.
Translator's Note: For advanced applications of macros, please refer to "Advanced Applications of C Language Macros".
The usage patterns given below can avoid some problems when using macros and are for reference when using macros:

  1. Do not define macros in .h files;
  2. #define correctly before use, #undef correctly after use;
  3. Don't just #undef an existing macro, choose a name that won't conflict;
  4. Don't use macros that can cause unbalanced C++ constructs (Translator's Note), at least document their behavior.
    5.15.0 and NULL (0 and NULL)
    Use 0 for integers, 0.0 for real numbers, NULL for pointers, and '\0' for characters (strings).
    There is no dispute that 0 is used for integers and 0.0 for real numbers.
    For pointers (address values), whether to use 0 or NULL, Bjarne Stroustrup recommends using the original 0. We recommend using NULL, which looks like a pointer. In fact, some C++ compilers (such as gcc 4.1.0) specifically provide The definition of NULL can give useful warnings, especially when sizeof(NULL) and sizeof(0) are not equal.
    Use '\0' for characters (strings), which not only has the correct type but also has good readability.

5.16.sizeof

Use sizeof(varname) instead of sizeof(type) whenever possible.
Sizeof(varname) is used because the code is automatically synchronized when the variable type changes. In some cases, sizeof(type) may make sense, but it should be avoided as much as possible. If the variable type changes, it cannot be synchronized.
Struct data;
memset(&data, 0, sizeof(data));
memset(&data, 0, sizeof(Struct));

5.17.Boost library (Boost)

Only use approved libraries in Boost.
Definition: The Boost library is a very popular, peer-reviewed, free, open source C++ library.
Advantages: Boost code quality is generally higher, portability is good, and it fills many gaps in the C++ standard library, such as type traits, more complete binders, better smart pointers, and also provides Implementation of TR1 (an extension to the standard library).
Disadvantages: Some Boost libraries promote poor readability of programming practices, such as metaprogramming and other advanced template techniques, and an overly "functional" programming style.
Conclusion: In order to provide better readability to those who read and maintain the code, we only allow the use of a mature subset of Boost features. Currently, these libraries include:

  1. Compressed Pair:boost/compressed_pair.hpp;
  2. Pointer Container: boost/ptr_container does not include ptr_array.hpp and serialization.
    We will actively consider adding Boost features where possible, so you don't have to stick to this rule.

5.18.Summary

  1. For smart pointers, safety first, convenience second, and be as localized as possible (scoped_ptr);
  2. The reference parameter is const, otherwise the pointer parameter is used;
  3. The use of function overloading should be clear and easy to read;
  4. In view of the easy misuse, the use of default function parameters is prohibited (questionable);
  5. The use of variable-length arrays is prohibited;
  6. Reasonable use of friends;
  7. In order to facilitate code management, the use of exceptions is prohibited (debatable);
  8. It is forbidden to use RTTI, otherwise redesign the code;
  9. Use C++-style type conversion and do not use dynamic_cast except for unit tests;
  10. Use stream or printf + read/write, it is a problem;
  11. Pre-increment/decrease can be used but post-increment/decrease is not required;
  12. Use const if it can be used, and advocate const first;
  13. Use integer types of a certain size and do not use unsigned types except bytes;
  14. When formatting output and structure alignment, pay attention to the system differences between 32-bit and 64-bit systems;
  15. Try to avoid using macros except for stringification and concatenation;
  16. Use 0 for integers, 0.0 for real numbers, NULL for pointers, and '\0' for characters (strings);
  17. Use sizeof(varname) instead of sizeof(type);
  18. Only use approved libraries in Boost.

6. Naming Convention

The most important consistency rule is naming management. The naming style can directly determine whether the named entity is: type, variable, function, constant, macro, etc., without looking for entity declarations. The pattern matching engine in our brain relies on these naming rules.
Naming rules are somewhat arbitrary, but consistency is more important than naming according to personal preference, so no matter what you think, rules are rules.

6.1. General Naming Rules

Function naming, variable naming, and file naming should be descriptive and not overly abbreviated. Types and variables should be nouns, and function names can use "imperative" verbs.
How to name:
Give a descriptive name as much as possible, don't save space, it is more important to let others understand your code quickly, good naming choices:
int num_errors; // Good.
int num_completed_connections; // Good.
Use vague names for ugly names abbreviation or random characters:
int n; // Bad - meaningless.
int nerr; // Bad - ambiguous abbreviation.
int n_comp_conns; // Bad - ambiguous abbreviation.
Types and variable names are generally nouns: such as FileOpener, num_errors.
Function names are usually directive, such as OpenFile(), set_num_errors(). Access functions need to be described in more detail and must match the variables they access.
Abbreviations:
Don’t use abbreviations unless they are obvious outside the project, for example:
// Good
// These show proper names with no abbreviations.
int num_dns_connections; // Most people know what “DNS” stands for.
int price_count_reader; / / OK, price count. Makes sense.

// Bad!
// Abbreviations can be confusing or ambiguous outside a small group.
int wgc_connections; // Only your group knows what this stands for.
int pc_reader; // Lots of things can be abbreviated “pc”.
不要用省略字母的缩写:
int error_count; // Good.
int error_cnt; // Bad.

6.2.File Names

The file name must be all lowercase and may contain underscores (_) or dashes (-), as per project convention.
Acceptable file naming:
my_useful_class.cc
my-useful-class.cc
myusefulclass.cc
C++ files end with .cc and header files end with .h.
Do not use file names that already exist under /usr/include (Translator's Note, for UNIX, Linux and other systems), such as db.h.
Generally, try to make the file name more clear. http_server_logs.h is better than logs.h. When defining a class, the file name usually appears in pairs, such as foo_bar.h and foo_bar.cc, corresponding to the class FooBar.
Inline functions must be placed in the .h file. If the inline function is short, place it directly in the .h file. If the code is relatively long, it can be placed in a file ending with -inl.h. For classes containing lots of inline code, there can be three files:
url_table.h // The class declaration.
url_table.cc // The class definition.
url_table-inl.h // Inline functions that include lots of code.
Refer to the first Chapter-inl.h file section.

6.3.Type Names

Each word in the type name starts with a capital letter and does not contain underscores: MyExcitingClass, MyExcitingEnum.
All type names—classes, structs, typedefs, enumerations—use the same convention, for example:
// classes and structs
class UrlTable { …
class UrlTableTester { …
struct UrlTableProperties { …

// typedefs
typedef hash_map<UrlTableProperties *, string> PropertiesMap;

// enums
enum UrlTableErrors { …

6.4. Variable Names

Variable names are always lowercase, words are connected with underscores, and class member variables end with underscores, such as my_exciting_local_variable, my_exciting_member_variable_.
Ordinary variable naming:
Example:
string table_name; // OK - uses underscore.
string tablename; // OK - all lowercase.
string tableName; // Bad - mixed case.
Class data members:
The data members of the structure can be the same as ordinary variables , no need to end with an underscore like a class:
struct UrlTableProperties { string name; int num_entries; } For a discussion of structures and classes, please refer to the section "Structures vs. Classes" in the third article. Global variables: There are no special requirements for global variables. Use them sparingly. They can be prefixed with g_ or other signs that are easy to distinguish from local variables.





6.5. Constant Names

Prefix the name with k: kDaysInAWeek.
All compile-time constants (whether local, global or within a class) remain slightly different from other variables, k is followed by a word starting with a capital letter: const int kDaysInAWeek
= 7;

6.6. Function Names

Regular functions (regular functions, translator's note, here as opposed to special functions such as access functions) have mixed case, while accessors and mutators are required to match the variable name: MyExcitingFunction(), MyExcitingMethod(), my_exciting_member_variable(), set_my_exciting_member_variable().
Ordinary function:
The function name starts with a capital letter, the first letter of each word is capitalized, and there is no underline:
AddTableEntry()
DeleteUrl()
Access function:
The access function must match the accessed variable name. Here is an excerpt of an instance variable num_entries_ Class:
class MyClass { public: int num_entries() const { return num_entries_; } void set_num_entries(int num_entries) { num_entries_ = num_entries; }



private:
int num_entries_;
};
Other short inline function names can also use lowercase letters. For example, calling such a function in a loop does not even need to cache its value, and lowercase naming is acceptable.
Translator's Note: It can be seen from this point that the lowercase function name means that it can be used directly inline.

6.7. Namespace Names

The name of the namespace is all lowercase and is named based on the project name and directory structure: google_awesome_project.
For a discussion of namespaces and how to name them, refer to Part 2: Namespaces.

6.8. Enumerator Names

Enumeration values ​​should be in all uppercase letters, with words separated by underscores: MY_EXCITING_ENUM_VALUE.
The enum name is of type, hence mixed case: UrlTableErrors.
enum UrlTableErrors { OK = 0, ERROR_OUT_OF_MEMORY, ERROR_MALFORMED_INPUT, };



6.9. Macro Names

You're not going to use macros, right? If used, like this: MY_MACRO_THAT_SCARES_SMALL_CHILDREN.
Refer to the fourth chapter of preprocessing macros. Macros are usually not used. If they are absolutely necessary, their names should be in all capital letters and underlined like enumeration names:
#define ROUND(x) …
#define PI_ROUNDED 3.0

6.10.Exceptions to Naming Rules

When naming objects similar to existing C/C++ entities, you can refer to existing naming conventions:
bigopen()
function name, refer to open()
uint
typedef type definition
bigpos
struct or class, refer to pos
sparse_hash_map
STL similar entities; refer to STL naming Conventional
LONGLONG_MAX
constant, similar to INT_MAX

6.11.Summary

  1. General rule: Do not abbreviate at will. If it is excusable to write ChangeLocalValue as ChgLocVal, writing ModifyPlayerName as MdfPlyNm is too much. Except for function names that can be appropriately verbs, try to use clear and easy-to-understand nouns for other names; 2. Macros
    , Use all caps + underscores for enumerations, etc.;
  2. Use all lowercase letters + underscore for variables (including class and structure member variables), files, namespaces, access functions, etc. Class member variables end with an underscore, and global variables start with g_;
  3. Ordinary functions, types (including classes, structures, enumeration types), constants, etc. use mixed case, without underscores;
  4. Reference existing or similar naming conventions.

7. Comments

Although comments are painful to write, they are crucial to ensuring code readability. The following rules describe what comments should be made and where they should be. Of course, remember that comments are indeed important, but the best code is self-documenting. It is much better to have clear type and variable names than to explain ambiguous names through comments.
Comments are written for someone else (the next person who needs to understand your code), so be serious, that next person might be you!

7.1.Comment Style

Use // or /* / to unify.
// or /
*/ can be used, // is just used more widely to ensure uniformity in how to comment and the comment style.

7.2. File Comments

Add a copyright notice at the beginning of each file, followed by a description of the file's contents.
Legal Notices and Author Information:
Each document contains the following items, in order:

  1. Copyright (copyright statement): such as Copyright 2008 Google Inc.;
  2. License version (license boilerplate): Choose the appropriate license version for the project, such as Apache 2.0, BSD, LGPL, GPL;
  3. Author (author line): Identifies the original author of the file.
    If you make significant changes to a file created by someone else, add your information to the author information so others know who to contact if they have questions about the file.
    File content:
    After each file’s copyright permission and author information, a comment describing the file content must be provided.
    Usually, the .h file should give a brief description of the functions and usage of the declared class, and the .cc file contains more implementation details or algorithm discussions. If you feel that these implementation details or algorithm discussions are helpful for reading, you can put . Comments in cc are placed in .h and indicate in .cc that the document is in .h.
    Do not simply copy comments between .h and .cc. The copied comments will deviate from the actual meaning.

7.3. Class Comments

Each class definition is accompanied by comments describing the functionality and usage of the class.
// Iterates over the contents of a GargantuanTable. Sample usage:
// GargantuanTable_Iterator* iter = table->NewIterator();
// for (iter->Seek(“foo”); !iter->done(); iter- >Next()) { // process(iter->key(), iter->value()); // } // delete iter; class GargantuanTable_Iterator { }; If you feel that this has been described in detail at the top of the file Class, if you want to simply say "see the top of the file for a complete description", you should add some comments in the class. If the class has any synchronization assumptions, document them. If instances of this class can be accessed by multiple threads, be sure to pay attention to the documentation when using them.







7.4. Function Comments

The comments at the function declaration describe the function function, and the function definition describes the function implementation.
Function declaration:
A comment is placed before the declaration to describe the function and usage. The comment uses a description ("Opens the file") rather than an instruction ("Open the file"); the comment is only to describe the function rather than telling the function what to do. Usually comments don't describe how the function is implemented, that's part of the definition.
The content of the annotation at the function declaration:

  1. inputs (input) and outputs (output);
  2. For class member functions: whether the object needs to maintain reference parameters during the function call and whether these parameters will be released;
  3. If the function allocates space, it needs to be released by the caller;
  4. Whether the parameter can be NULL;
  5. Are there any performance implications of using the function?
  6. 如果函数是可重入的(re-entrant),其同步前提(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;

But don't have unnecessary redundant or obvious comments. There is no need to add "returns false otherwise" to the following comment, because it is already implicit:
// Returns true if the table cannot hold any more entries.
bool IsTableFull();

When commenting constructors/destructors, remember that the person reading the code knows what the constructor/destructor is, so a comment like "destroys this object" is meaningless. Explain what the constructor does to the parameters (for example, whether it is the owner of the pointer) and what the destructor cleans up. If it is irrelevant, just omit the comment. It is normal to have no comment before the destructor.
Function definition:
When defining each function, comments should be used to explain the function function and implementation points, such as the beautiful code used, the brief steps of implementation, the reason for such implementation, and why the first half needs to be locked but the second half does not.
Do not copy comments directly from the function declaration in the .h file or elsewhere. It is okay to briefly describe what the function does, but the focus should be on how to implement it.

7.5. Variable Comments

Usually the variable name itself is enough to describe the purpose of the variable. In certain cases, additional comments are needed.
Class data members:
Each class data member (also called instance variable or member variable) should be commented to explain its purpose. If the variable can accept sentinel values ​​such as NULL or -1, it must be explained, such as: private:
//
Keeps track of the total number of entries in the table.
// Used to ensure we do not go over the limit. -1 means
// that we don't yet know how many entries the table has.
int num_total_entries_;
Global variable (constant ):
Similar to data members, all global variables (constants) should also be commented with their meaning and purpose, such as:
// The total number of tests cases that we run through in this regression test.
const int kNumTestCases = 6;

7.6.Implementation Comments

Comment out clever, obscure, interesting, and important places in the implementation code.
Comments before code:
Comments should be added before outstanding or complex code blocks, such as:
// Divide 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; } Line comment: rather obscure If you want to add a comment at the end of the line, you can add an end-of-line comment with two spaces after the code, such as: // If we have enough memory, mmap the data portion too. mmap_budget = max(0, mmap_budget - index_->length() ); if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytes, mlock)) return; // Error already logged.









Notice that there are two blocks of comments describing this code, and the comments mention that errors have been logged when the function returns.
There are comments on the adjacent lines before and after, which can be adjusted appropriately to make them more readable:

DoSomething(); // Comment here so the comments line up.
DoSomethingElseThatIsLonger(); // Comment here so there are two spaces between
/ / the code and the comment.

NULL, true/false, 1, 2, 3...:
When passing in a Boolean value or integer to the function, you should comment out the meaning, or use constants to make the code understandable. Compare:
bool success = CalculateSomething(interesting_value,
10,
false,
NULL); // What are these arguments??

和:
bool success = CalculateSomething(interesting_value,
10, // Default base value.
false, // Not the first time we’re calling this.
NULL); // No callback.

Use constants or descriptive variables:
const int kDefaultBaseValue = 10;
const bool kFirstTimeCalling = false;
Callback *null_callback = NULL;
bool success = CalculateSomething(interesting_value,
kDefaultBaseValue,
kFirstTimeCalling,
null_callback);

Don't:
Be careful never to use natural language translation code as comments, assume that the person reading your code is better at C++ than you :D: //
Now go through the b array and make sure that if i occurs,
// the next element is i +1.
… // Geez. What a useless comment.

7.7. Punctuation, Spelling and Grammar

Pay attention to punctuation, spelling, and grammar; well-written comments are much easier to read than poorly written ones.
Comments are generally complete sentences containing appropriate capitalization and periods (.). Shorter comments (such as comments at the end of lines of code) can be clicked as you like, but you must still pay attention to the consistency of style. Complete sentences are more readable and indicate that the comment is complete rather than a half-baked idea.
Although it is a bit embarrassing to use a comma when someone pointed out that a semicolon should be used. Clear and readable code is still important, and proper punctuation, spelling, and grammar can help with this.

7.8.TODO Comments

Use TODO comments for temporary, short-term solutions, or code that is good enough but not perfect.
Such comments should use the all-capital string TODO, followed by your name, email address, etc. in brackets (parentheses), and you can also add a colon (colon): the purpose is to search according to the unified TODO format: //
TODO ([email protected]): Use a “*” here for concatenation operator.
// TODO(Zeke) change this to use relations.

If you are adding it to "do something someday in the future", you can add a specific time ("Fix by November 2005") or event ("Remove this code when all clients can handle XML responses.").

7.9.Summary

  1. Regarding comment style, many C++ coders prefer line comments. C coders may still have a soft spot for block comments, or use block comments when commenting on large sections of file headers; 2. File comments can show off your achievements
    . It’s also so that others can look for you if you poke trouble;
  2. Comments should be concise and to the point, without being procrastinating or redundant. Simplifying complex things and complicating simple things will be despised;
  3. For Chinese coders, it is a problem whether to comment in English or in Chinese. But no matter what, comments are for others to understand. Is it to show off your mother tongue or foreign language proficiency other than programming languages?
  4. Comments should not be too messy. Appropriate indentation will make people happy to read them. However, there is no need to specify which column the comments should start from (I always like this when I write code). Under UNIX/LINUX, you can also agree on whether to use tab or space, I personally prefer space;
  5. TODO is very good. Sometimes, comments are really used to mark some unfinished or unsatisfactory completed areas. In this way, after searching, you will know what work remains to be done, and logs will be saved.

8.Format

Code style and format are indeed relatively arbitrary, but it is very easy for everyone in a project to follow the same style. As an individual, you may not agree with every part of the following format rules, but it is important for the entire project to obey a unified programming style. This makes it easier for everyone to read and understand the code.

8.1.Line Length

The number of characters in each line of code should not exceed 80.
We also recognize that this rule is controversial, but with so much code following it, we feel consistency is more important.
Pros: Advocates of this principle think it's barbaric to force them to resize their editor window. Many people open several windows side by side at the same time, and there is no extra space to widen a certain window. People limit the maximum size of the window and consistently use 80 columns wide. Why should we change it?
Disadvantages: Opponents of this principle argue that wider lines of code are easier to read and that the 80-column limit is an archaic flaw of mainframe computers from the 1960s; modern devices have wider displays and can easily display more. code.
Conclusion: 80 characters is the maximum. exception:

  1. If a line of comments contains a command or URL that exceeds 80 characters, it can exceed 80 characters for the convenience of copying and pasting;
  2. Those containing long paths can exceed 80 columns, so try to avoid them;
  3. Header file protection (preventing repeated inclusion of the first article) can ignore this principle.

8.2.Non-ASCII Characters

Try not to use non-ASCII characters. When using them, you must use UTF-8 format.
User interface text should not be hard-coded into the source code, even if it is in English, so non-ASCII characters should be used sparingly. Such characters can be included appropriately in special cases. For example, when the code analyzes external data files, the non-ASCII strings used as delimiters in the data files can be appropriately hardcoded; more commonly, unit test code (that does not require localization) may Contains non-ASCII strings. In such cases, UTF-8 should be used as its encoding can be understood and processed by many tools, as can hexadecimal encoding, especially if it enhances readability - as in "\xEF\xBB\ xBF" is a Unicode zero-width no-break space character that is invisible when included in a source file in UTF-8 format.

8.3. Spaces vs. Tabs

Use only spaces and indent 2 spaces at a time.
Use spaces for indentation, don't use tabs in your code, and set your editor to convert tabs to spaces.

8.4. Function Declarations and Definitions

The return type goes on the same line as the function name and, if appropriate, the parameters.
The function looks like this:
ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) { DoSomething(); } If there is too much text in the same line to accommodate all parameters: ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2, Type par_name3) { DoSomething(); } Even the first parameter cannot fit: ReturnType LongClassName::ReallyReallyReallyLongFunctionName( Type par_name1, // 4 space indent Type par_name2, Type par_name3) { DoSomething(); // 2 space indent } Note the following points:


















  1. The return value is always on the same line as the function name;
  2. The left parenthesis (open parenthesis) is always on the same line as the function name;
  3. There is no space between the function name and the left parenthesis;
  4. There are no spaces between parentheses and parameters;
  5. The open curly brace is always at the end of the same line as the last parameter;
  6. The close curly brace is always located alone on the last line of a function;
  7. There is always a space between the closing parenthesis and the opening brace;
  8. All formal parameter names at function declaration and implementation must be consistent;
  9. All formal parameters should be aligned as much as possible;
  10. The default indentation is 2 spaces;
  11. Independently packaged parameters remain indented by 4 spaces.
    If the function is const, the keyword const should be on the same line as the last parameter.
    // Everything in this function signature fits on a single line
    ReturnType FunctionName(Type par) const { }

// This function signature requires multiple lines, but
// the const keyword is on the line with the last parameter.
ReturnType ReallyLongFunctionName(Type par1,
Type par2) const { } If some parameters are not used, change the parameters at the function definition Annotate the name: // Always have named parameters in interfaces. class Shape { public: virtual void Rotate(double radians) = 0; }







// Always have named parameters in the declaration.
class Circle : public Shape {
public:
virtual void Rotate(double radians);
}

// Comment out unused named parameters in definitions.
void Circle::Rotate(double /radians/) {}
// Bad - if someone wants to implement later, it’s not clear what the
// variable means.
void Circle::Rotate(double) {}

8.5. Function Calls

Try to put them on the same line, otherwise, enclose the actual parameters in parentheses.
Function calls follow the following form:
bool retval = DoSomething(argument1, argument2, argument3);

If the same line cannot fit, it can be broken into multiple lines. Each subsequent line should be aligned with the first actual parameter. Do not leave a space after the left parenthesis and before the right parenthesis:
bool retval = DoSomething(averyveryverylongargument1,
argument2, argument3);
if There are many function parameters. For the sake of readability, you can put only one parameter in each line:
bool retval = DoSomething(argument1,
argument2,
argument3,
argument4);
If the function name is too long and exceeds the maximum line length, you can put all Parameters are in separate lines:
if (…) { if (…) { DoSomethingThatRequiresALongFunctionName( very_long_argument1, // 4 space indent argument2, argument3, argument4); }








8.6. Conditional statements (Conditionals)

It is also recommended not to add spaces in parentheses, and to put the keyword else on a new line.
There are two acceptable formats for basic conditional statements, one with spaces between the parentheses and the condition, and one without.
The most common format is the format without spaces. Any format is acceptable, but consistency is the key. If you are modifying a file, refer to the current format; if you are writing new code, refer to the format of other files in the directory or project. If you are still wondering, don't add spaces.
if (condition) { // no spaces inside parentheses
… // 2 space indent.
} else { // The else goes on the same line as the closing brace.

}

If you prefer spaces inside parentheses:
if ( condition ) { // spaces inside parentheses - rare
… // 2 space indent.
} else { // The else goes on the same line as the closing brace.

}

Note that in all cases there is a space between if and the left parenthesis, and also between the right parenthesis and the left curly brace (if used):
if(condition) // Bad - space missing after IF.
if (condition) { // Bad - space missing before {.
if(condition){ // Doubly bad.
if (condition) { // Good - proper space after IF and before {.
Some conditional statements are written on the same line to enhance readability, Use only when the statement is simple and does not use an else clause:
if (x == kFoo) return new Foo();
if (x == kBar) return new Bar();
If the statement has an else branch, it is not allowed:
/ / Not allowed - IF statement on one line when there is an ELSE clause
if (x) DoThis();
else DoThat();
Usually, single-line statements do not need to use curly brackets. It is understandable if you like it. Some people also require that if must be used. Braces:
if (condition)
DoSomething(); // 2 space indent.

if (condition) {
DoSomething(); // 2 space indent.
}

But if any branch of the statement uses curly braces, other parts must also use them:
// Not allowed - curly on IF but not ELSE
if (condition) { foo; } else bar;


// Not allowed - curly on ELSE but not IF
if (condition)
foo;
else {
bar;
}

// Curly braces around both IF and ELSE required because
// one of the clauses used braces.
if (condition) {
foo;
} else {
bar;
}

8.7. Loops and Switch Statements

The switch statement can be divided into blocks using braces; the empty loop body should use {} or continue.
The case block in the switch statement may or may not use curly braces, depending on your preference. When using it, follow the instructions below.
If there is a value that does not meet the case enumeration conditions, always include a default (if there is an input value that is not processed by the case, the compiler will give an alarm). If default will never be executed, you can simply use assert:
switch (var) { case 0: { // 2 space indent … // 4 space indent break; } case 1: { break; } default: { assert(false ); } } The empty loop body should use {} or continue instead of a simple semicolon: while (condition) { // Repeat test until it returns false. } for (int i = 0; i < kSomeNumber; ++ i) {} // Good - empty body. while (condition) continue; // Good - continue indicates no logic.


















while (condition); // Bad - looks like part of do/while loop.

8.8. Pointers and Reference Expressions

There should be no spaces before and after the period (.) or arrow (->), and there should be no spaces after the pointer/address operators (*, &).
The following are correct examples of pointer and reference expressions:
x = *p;
p = &x;
x = ry;
x = r->y;

Notice:

  1. When accessing members, there are no spaces before or after the period or arrow;
  2. There is no space after the pointer operator * or &.
    When declaring pointer variables or parameters, the asterisk can appear immediately next to the type or variable name:
    // These are fine, space preceding.
    char *c;
    const string &str;

// These are fine, space following.
char* c; // but remember to do “char* c, *d, *e, …;”!
const string& str;
char * c; // Bad - spaces on both sides of *
const string & str; // Bad - spaces on both sides of &
must be at least consistent in the same file (new or existing).

8.9. Boolean Expressions

If a Boolean expression exceeds the standard line width (80 characters), line breaks must be unified.
In the following example, the logical AND (&&) operator is always located at the end of the line:
if (this_one_thing > this_other_thing &&
a_third_thing == a_fourth_thing &&
yet_another & last_one) { } Both logical AND (&&) operators are located at the end of the line, which can be considered Inserting extra parentheses can be very helpful to enhance readability if used appropriately.


8.10. Function return values ​​(Return Values)

Do not use parentheses in return expressions.
Do not use parentheses when a function returns:
return x; // not return(x);

8.11. Variable and Array Initialization

Choose = or ().
You need to choose between the two. The following forms are correct:
int x = 3;
int x(3);
string name(“Some Name”);
string name = “Some Name”;

8.12. Preprocessor Directives

Do not indent preprocessing directives, start at the beginning of the line.
Even if preprocessing directives are within an indented block of code, the directives should start at the beginning of the line.
// Good - directives at beginning of line
if (lopsided_score) { #if DISASTER_PENDING // Correct – Starts at beginning of line DropEverything(); #endif BackToNormal(); } // Bad - indented directives if (lopsided_score) { #if DISASTER_PENDING // Wrong! The “#if” should be at beginning of line DropEverything(); #endif // Wrong! Do not indent “#endif” BackToNormal(); }











8.13. Class Format

The declared attributes are public:, protected:, private: in order, indented by 1 space each time (Translator's note, why not two? Some people advocate private first, so that it is clear at a glance which data members are declared, and some people advocate It makes sense to put variables and operations together according to logical relationships :-)).
The basic format of a class declaration (if you don’t know about class annotations, refer to the Class Annotations section in Part 6) is as follows:
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_;
DISALLOW_COPY_AND_ASSIGN(MyClass);
};
注意:

  1. Therefore, the base class name should be placed on the same line as the subclass name as much as possible within the 80-column limit;
  2. Keywords public:, protected:, private: should be indented by 1 space (Translator's note, MSVC mostly uses tab indentation, and these three keywords are not indented);
  3. Except for the first keyword (usually public), leave a blank line before other keywords. If the class is relatively small, it does not need to be blank;
  4. Do not leave a blank line after these keywords;
  5. Public is placed first, followed by protected and private;
  6. For the order of declaration, please refer to the section "Declaration Order" in Part 3.

8.14.Initializer Lists

Put the constructor initialization list on the same line or on several lines indented by four spaces.
Two acceptable initialization list formats:
// When it all fits on one line:
MyClass::MyClass(int var) : some_var_(var), some_other_var_(var + 1) { or // When it requires multiple lines, indent 4 spaces, putting the colon on // the first initializer line: MyClass::MyClass(int var) : some_var_(var), // 4 space indent some_other_var_(var + 1) { // lined up DoSomething(); }









8.15. Namespace Formatting

Namespace contents are not indented.
Namespaces do not add additional indentation levels, for example:
namespace {

void foo() { // Correct. No extra indentation within namespace.

}

} // namespace

Do not indent:
namespace {

// Wrong. Indented when it should not be.
void foo() {

}

} // namespace

8.16. Horizontal Whitespace

水平留白的使用因地制宜。不要在行尾添加无谓的留白。
普通:
void f(bool b) { // Open braces should always have a space before them.

int i = 0; // Semicolons usually have no space before them.
int x[] = { 0 }; // Spaces inside braces for array initialization are
int x[] = {0}; // optional. If you use them, put them on both sides!
// 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.

Adding redundant white space will create an extra burden for others to edit, so don't include extra white space. If you are sure that a line of code has been modified, remove the extra spaces; or remove them when specifically cleaning up spaces (make sure no one else is using it).
Loops and conditional statements:
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) { switch ( i ) { // Loops and conditions may have spaces inside if ( test ) { // parentheses, but this is rare. Be consistent. for ( int i = 0; i < 5; ++i ) { for ( ; i < 5 ; ++i) { // For loops always have a space after the … // semicolon, and may have a space before the / / semicolon. 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.
操作符:
x = 0; // Assignment operators always have spaces around
// them.
x = -5; // No spaces separating unary operators and their
++x; // arguments.
if (x && !y)

v = w * x + y / z; // Binary operators usually have spaces around them,
v = wx + y/z; // but it’s okay to remove spaces around factors.
v = w * (x + z); // Parentheses should have no spaces inside them.
模板和转换:
vector x; // No spaces inside the angle
y = static_cast<char
>(x); // brackets (< and >), before
// <, or between >( in a cast.
vector<char *> x; // Spaces between type and pointer are
// okay, but be consistent.
set<list > x; // C++ requires a space in > >.
set< list > x; // You may optionally make use
// symmetric spacing in < <.

8.17. Vertical Whitespace

The less vertical white space the better.
This is not just a rule but a matter of principle: don't use blank lines unless absolutely necessary. In particular: do not leave more than 2 blank lines between two function definitions, do not have blank lines at the beginning and end of the function body, and do not add blank lines at will in the function body.
The basic principle is: the more code that can be displayed on the same screen, the easier it is to understand the control flow of the program. Of course, a block of code that is too dense is just as unsightly as a block that is too sparse, so it’s up to your discretion, but generally less is better.
There should be no blank lines at the beginning and end of the function:
void Function() {

// Unnecessary blank lines before and after

}
There should be no blank lines at the beginning and end of the code block:
while (condition) { // Unnecessary blank line after

}
if (condition) {

// Unnecessary blank line before
}

A blank line between if-else blocks is also acceptable:
if (condition) { // Some lines of code too small to move to another function, // followed by a blank line.

} else {
// Another block of code
}

8.18.Summary

  1. In principle, the row width should not exceed 80 columns, which would take up the entire 22-inch display screen, which is unjustifiable;
  2. Try not to use non-ASCII characters. If you use them, refer to the UTF-8 format (especially under UNIX/Linux, wide characters can be considered under Windows), and try not to couple string constants into the code, such as creating independent resource files. This It’s not just a matter of style anymore;
  3. Use spaces unconditionally under UNIX/Linux, and there is nothing wrong with using Tab under MSVC;
  4. Function parameters, logical conditions, and initialization lists: either all parameters and function names are placed on the same line, or all parameters are separated into separate lines;
  5. In addition to the opening brace of function definitions that can be placed at the beginning of the line, the opening braces including function/class/structure/enumeration declarations and various statements can be placed at the end of the line, and all closing braces are on separate lines;
  6. Do not leave spaces before and after the ./-> operator. Do not leave spaces before and after */&. Just one operator is enough. You can choose left or right according to your preference;
  7. Preprocessing directives/namespaces do not use additional indentation, classes/structures/enumerations/functions/statements use indentation;
  8. Whether to use = or () for initialization depends on personal preference, just keep it consistent;
  9. Do not add () to return;
  10. Don’t abuse horizontal/vertical white space. Make it easier to read.

9.Exceptions to the rules

The coding practices explained earlier are basically mandatory, but all good rules allow exceptions.

9.1. Existing Non-conformant Code

Existing code that does not conform to the established programming style can be tolerated.
When you modify code that uses other styles, you don't need to use the conventions in this guide to keep it consistent with the original style of the code. If you are worried, you can discuss it with the original author of the code or the current person in charge. Remember, consistency includes the original consistency.

9.2.Windows Code

Windows programmers have their own coding habits, mainly derived from some Windows header files and other Microsoft code. We want anyone to be able to read your code, so we have a separate guide to C++ coding for all platforms.
If you have been using Windows coding style, it is necessary to reiterate some guidelines that you may have forgotten (Translator's Note, why I feel like I am being brainwashed :D):

  1. Do not use Hungarian notation (such as defining an integer variable as iNum), use Google naming conventions, including using the .cc extension for source files;
  2. Windows has defined many synonyms for the original built-in types (Translator's note, I am also disgusted with this), such as DWORD, HANDLE, etc. This is completely acceptable and even encouraged when calling Windows API, but you should still try to use it. Original C++ types, for example, use const TCHAR* instead of LPCTSTR;
  3. When compiling with Microsoft Visual C++, set the warning level to 3 or higher and treat all warnings as errors;
  4. Do not use #pragma once; as include protection, use C++ standard include protection, and include the protected file path to the top level of the project tree (Translator's Note, #include<prj_name/public/tools.h>);
  5. Do not use any non-standard extensions, such as #pragma and __declspec, unless absolutely necessary. It is allowed to use __declspec(dllimport) and __declspec(dllexport), but macros such as DLLIMPORT and DLLEXPORT must be passed so that others can use these codes when sharing It is easy to abandon these extensions.
    On Windows, there are only a few rules that can occasionally be broken:
  6. Usually we are prohibited from using multiple inheritance, but you can use multiple inheritance when using COM and ATL/WTL classes. In order to execute COM or ATL/WTL classes and their interfaces, you can use multiple implementation inheritance;
  7. Although exceptions should not be used in the code, exceptions are widely used in ATL and some STL (including Visual C++'s STL). When using ATL, _ATL_NO_EXCEPTIONS should be defined to block exceptions. You need to study whether STL exceptions are also blocked. , if not shielded, you can also enable compiler exceptions. Note that this is only for compiling STL. You still should not write code containing exception handling;
  8. Usually each source file of each project contains a header file named StdAfx.h or precompile.h to facilitate header file pre-compilation. In order to make the code easy to share with other projects, avoid explicitly including this file (precompile.cc Except), use compiler option /FI to automatically include;
  9. Resource header files, usually named resource.h and containing only macros, do not need to adhere to this style guide.

9.3.Teamwork

Use common sense and be consistent.
When editing code, take a moment to look at other code in the project and determine its style. If other code uses spaces in if statements, use them too. If the comments are enclosed in a box
with asterisks ( ), you do the same: / ***************************** ****

  • Some comments are here.
  • There may be many lines.
    ************************************/
    The point of using a programming style guide is to provide a With common coding standards, everyone can focus on implementing content rather than presentation. We have given global style specifications, but local style is also very important. If the new code you add in a file is far from the original code style, this will destroy the overall beauty of the file itself and affect reading. So try to avoid it.
    Okay, I’ve almost written about coding style. The code itself is more interesting, so enjoy it!

Guess you like

Origin blog.csdn.net/weixin_30197685/article/details/132507070