Google C++ Style excerpt notes

Original link: https://www.luozhiyun.com/archives/698

Why do this excerpt? Because when I was learning C++ code, I found that its functional advantages are too powerful, and there are no strong restrictions on code writing. After decades of iteration, C++ actually bears a lot of old times, which also leads to different backgrounds in different times. People from different countries have different habits in writing C++ code, so I want to learn from how excellent teams write C++ code as much as possible, so I have this excerpt.

inline function

Only define a function as inline if it has 10 lines or less.

Misuse of inlining will make the program slower. Inlining may increase or decrease the amount of object code, depending on the size of the inline function. Inline is very short

Small accessor functions generally reduce code size, but inlining a fairly large function will dramatically increase code size.

Also don’t inline functions that contain loops or switch statements, virtual functions, and recursive functions, which usually don’t inline well.

Try not to have naked global functions

Putting non-member functions in the namespace avoids polluting the global scope. This is actually quite important to reduce unnecessary coupling and link-time dependencies.

Pay attention to where the variable is declared

In general, it is advisable to declare variables in the smallest possible scope, as close to the first use as possible. This makes it easier for code viewers to locate variable declarations and to understand the type and initial value of the variable.

But there are some exceptions:

 // 低效的实现for (int i = 0; i < 1000000; ++i) { Foo f; // 构造函数和析构函数分别调用1000000 次! f.DoSomething(i); }

If the variable is an object, its constructor is called every time it enters the scope, and its destructor is called every time it exits the scope. It is much more efficient to declare such variables outside the scope of the loop:

 // 构造函数和析构函数只调用1 次Foo f; for (int i = 0; i < 1000000; ++i) { f.DoSomething(i); }

Static or global variables of type class are prohibited

The order of constructors, destructors, and initializations of global variables of class types is indeterminate in C++, and even varies with build changes, leading to hard-to-find bugs, and the order of destruction is also indeterminate.

class initialization

If member variables are defined in a class, you must provide an initialization function or define a constructor in the class for each class. If no constructor is declared, then

The compiler generates a default constructor, which may cause some members to be uninitialized or initialized to inappropriate values.

This is not like java or go, they all have a default initializer, but C++ does not, so if your class has member variables that are not initialized in the class, and no other constructor is provided, you must define one (without parameter) default constructor. It is certainly more reasonable to initialize the internal state of the object to a consistent/valid value.

Copyable and removable types

Make them support copy/move if your types require it. Otherwise, the implicitly generated copy and move functions are disabled.

Copy/move constructors generally have their specific uses, such as making code cleaner and performing better, but it’s an implicit operation that is often confusing and overlooked. If your class does not require copy/move operations, disable it explicitly via = delete or other means.

overloaded operator

Do not overload operators except in a few specific circumstances.

To make the code look more intuitive, classes can behave like built-in types (such as int) using operators such as + and /. But this implicit operation can be confusing, making you feel like a time-consuming operation is as light as manipulating built-in types. And it’s harder to locate the call site of an overloaded operator, finding Equals() is obviously much easier than the corresponding == call site.

So generally do not overload operators. In particular, assignment operations (operator=) are tricky and overloading should be avoided. Functions like Equals(), CopyFrom(), etc. can be defined if needed.

Don’t use C++ exceptions

When writing java projects in the past, I often used exceptions to handle errors, and then wrapped a large try catch in the outer layer to catch them, which means that if you want to use exceptions, you must check all call points, otherwise you will watch the exception happily all the way. Run up, and eventually interrupt the entire program.

And exceptions are easy to disrupt the execution flow of the program and difficult to judge, and the function may return at a place you don’t expect. And abusing exceptions will encourage developers to catch “pseudo exceptions” that are outdated or cannot be recovered in disguise.

Instead, we can use error codes, assertions, etc. to keep our program running.

Try to use C++ type conversions

Many students like to use C-style type conversion int y = (int)x or int y = int(x) in C++ code. But the problem with casts in C is ambiguous operations; sometimes casts (eg (int)3.5), sometimes casts (eg (int)”hello”).

So we should:

  • Use static_cast instead of C-style value casts, or when a class pointer needs to be explicitly upcast to a parent class pointer.
  • Use const_cast to remove the const qualifier.
  • Use reinterpret_cast to perform unsafe conversions between pointer types and integers or other pointers. only if you’ve done everything

    Use it at your heart.

The prefix increment (++i), decrement operator should be used

Regardless of the return value, pre-increment (++i) is usually more efficient than post-increment (i++). Because the post-increment (or decrement) requires 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 large. Of course, this statement is only for custom types. If it is a built-in type, most compilers will optimize it, so there is no difference in efficiency.

Don’t use unsigned integers

Do not use unsigned ints like uint32_t unless you are representing a bit group rather than a number or you need to define two’s complement

code overflow. In particular, don’t use an unsigned type just to point out that a number can never be negative. Instead, you should use assertions to protect data.

Be careful with integer type conversions and integer promotions (acgtyrant Note: integer promotions, such as int and unsigned int operations,

the former is promoted to unsigned int and may overflow), always with unintended consequences.

Regarding unsigned integers:

Some people, including some textbook authors, recommend using unsigned types for non-negative numbers. This practice attempts to be self-documenting. But when

In C, this advantage is overwhelmed by the bugs it causes. Take a look at the example below:

 for (unsigned int i = foo.Length()-1; i >= 0; --i) ... // 关于这点我还弄出过bug

The above loop never exits! Sometimes gcc will find the bug and report it, but most of the time it won’t. Similar bugs will also appear in

When comparing a conforming variable with an unsigned variable.

Try to replace #define with const, enum, inline

A lot of C++ code today still retains the habit of using macros in the C era. In C++, macros are not as essential as they are in C. In the past, macros used to expand performance-critical code can now be replaced by inline functions, and macros that represent constants can be replaced by const variables.

For a common example, we use the following macro:

 #define PI 3.14

As the above PI macro definition, when the program is compiled, the compiler will first replace all PI macro definitions in the source code with 3.14 in the preprocessing stage. After the program is compiled in the preprocessing stage, the real compilation stage is carried out. On some compilers that use this PI constant, if you get a compilation error, the error message may mention 3.14 instead of PI , which can be confusing, especially if the project is large.

In short, be very careful when using macros, try to inline functions, enums and constants instead.

For naming conventions and code style

Different teams have different specifications and code styles. You should try to refer to the original project instead of adding a new style yourself, which will feel very strange when reading the code. In order to be consistent with the original style of the code, if you are uneasy, you should discuss it with the original author of the code or the current responsible person.

Reference

http://staff.ustc.edu.cn/~tongwh/CG_2019/materials/Google%20C++%20Style%20Guide.pdf

Scan code_Search joint communication style-white version 1

The Google C++ Style excerpt notes first appeared on luozhiyun`s Blog .

This article is reproduced from: https://www.luozhiyun.com/archives/698
This site is for inclusion only, and the copyright belongs to the original author.

Leave a Comment