Google C ++ programming style guide (4): Smart pointers and other C ++ features

Source: Internet
Author: User

 

1. for smart pointers, security first, convenience second, localization (scoped_ptr) as much as possible );

2. Add const to the referenced parameter; otherwise, use the pointer parameter;

3. Use of function overloading should be clear and easy to read;

4. The use of default function parameters is prohibited due to misuse (it is worth discussing );

5. Do not use variable-length arrays;

6. Reasonable Use of youyuan ......

 

Unique Style of Google

Google has many of its own skills and functions to make C ++ code more robust, as well as the use of C ++ in a different way.

 

1. Smart pointers)

Scoped_ptr is fully qualified if you really need to use smart pointers. In special cases, for example, for objects in STL containers, you should only use STD: tr1: shared_ptr, and do not use auto_ptr in any case.

The "smart" pointer looks like a pointer, but it is actually an object with semantics attached. Taking scoped_ptr as an example, when scoped_ptr is destroyed, it deletes the object it points. This is also true for shared_ptr. In addition, shared_ptr implements reference-counting, so that the pointer is deleted only when the last object to which it points is destroyed.

In general, we tend to design code with clear object affiliation. the most explicit object affiliation means that no pointer is used at all and the object is directly used as a field or local variable. The other extreme is that the reference counting pointer does not belong to any object, so the design problem is that it may easily lead to loop reference or other strange conditions that make the object unable to be deleted, in addition, the atomic operation is slow every time a value is copied or assigned.

Although this is not recommended, reference to the Count pointer is the simplest and most effective solution in some cases.

Note: It seems that the difference between Google is to avoid using the smart pointer: D, which is also localized as much as possible, and is safe first.

Other C ++ features

1. Reference arguments)

Therefore, const must be added to parameters passed by reference.

Definition: in C language, if the function needs to modify the value of the variable, the parameter (parameter) must be a pointer, such as int Foo (int * pval ). In C ++, the function can also declare the reference parameter: int Foo (Int & Val ).

Advantage: defining parameters as references avoids ugly Code such as (* pval) ++, and applications such as copy constructors are also required, the NULL pointer is not accepted as the pointer.

Disadvantage: it is easy to misunderstand because the reference syntax is a value but has the semantics of the pointer.

Conclusion:

In the function parameter table, all references must be const:

Void Foo (const string & in, string * out );

In fact, this is a hard Convention: the input parameter is a value or constant reference, and the output parameter is a pointer. The input parameter can be a constant pointer, but cannot be a constant reference parameter.

Constant pointers can be used when it is emphasized that parameters are not copied and must always exist during the lifetime of an object. It is best to describe these in the annotations. STL adapters such as bind2nd and mem_fun do not accept reference parameters. In this case, the function must also be declared as pointer parameters.

 

 

2. Function Overloading)

You can only use overload functions (including constructors) when the input parameters have different types and functions. Do not use function overload to simulate default function parameters.

Definition: You can define a function parameter type as const string & and Its overload function type as const char *.

class MyClass {
public:
void Analyze(const string &text);
void Analyze(const char *text, size_t textlen);
};

Advantage: By reloading functions of the same name with different parameters, the code is more intuitive, The templated code needs to be reloaded, and it brings convenience to visitors.

Disadvantage: one reason to restrict the use of overload is that it is difficult to determine which function is called at a specific call, another reason is that when a derived class only loads some variables of a function, many people may be confused about the inheritance semantics. In addition, when reading the client code of the library, it is not necessary to understand the default function parameters.

Conclusion: If you want to reload a function, you need to include the parameter information in the function name. For example, use appendstring (), appendint (), and not append ().

 

3. default parameters (default arguments)

Do not use default function parameters.

Advantage: a function is often used with a large number of default values, and these values are occasionally overwritten. The default parameter provides the convenience of defining less functions for few exceptions.

Disadvantage: You often check the existing code to determine how to use the API. The default parameters make it difficult to present all parameters in the copied and pasted previous code. When the default parameters are not applicable to the new code, this may cause major problems.

Conclusion: All parameters must be explicitly specified, and the programmer is forced to consider the API and input parameter values to avoid using default parameters that may not be known to the programmer.

 

4. Variable Length array and alloca (variable-length arrays and alloca ())

Variable Length array and alloca () are not allowed ().

Advantage: The variable length array has the inherent syntax, and the variable length array and alloca () are also very efficient.

Disadvantage: The variable length array and alloca () are not components of the Standard C ++. More importantly, they are stored in the stack) memory leakage that may be hard to find due to the size of data allocation: "I ran well on my machine, but it was inexplicably suspended in the product ".

Conclusion:

Use a safe distributor (Allocator), such as scoped_ptr/scoped_array.

 

5. Friends)

Reasonable Use of friend functions and friends functions is allowed.

Usually, the user Meta is defined in the same file, so that readers do not need to go to other files to find their usage for a private member of a class. A common use of youyuan is to declare foobuilder as a friend of foo. foobuilder can correctly construct the internal state of Foo without exposing it. In some cases, it is convenient to declare a unit test class as a friend of the class to be tested.

Youyuan extends (but does not break) The encapsulation boundary of the class. When you want to only allow another class to access a member, it is usually much better to use youyuan than to declare it as public. Of course, most classes should only provide public members to interact with them.

 

 

 

6. Exceptions)

Do not use C ++ exceptions.

Advantages:

1) exceptions allow upper-layer applications to decide how to handle "impossible" failures in underlying nested functions, which is not as obscure as the records of error codes;

2) It is 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 third-party C ++ libraries are abnormal. Disabling exceptions will make it difficult to combine them;

4) exceptions are the only solution to constructor failure. Although exceptions can be simulated using the factory function or Init () method, however, they need heap allocation or a new "illegal" status;

5) in the testing framework, exceptions are really useful.

Disadvantages:

1) when a throw statement is added to an existing function, all calls must be checked, even if they have at least basic exception security protection or the program ends normally, it is never possible to catch the exception. For example, if F () callg () callh () and H throw an exception captured by F, G should be careful to avoid incomplete cleaning;

2) In other words, exceptions may cause control flow of the program to fail to be determined by checking the code: A function may return uncertain results in difficulty in code management and debugging. Of course, you can minimize the overhead by specifying when and where exceptions are used, but it puts the burden on developers to master these regulations;

3) For exceptional security, raiI and different coding practices are required. Writing exception security code easily and correctly requires a lot of support. Exception allowed;

4) adding exceptions increases the size of the binary Execution Code, increases the compilation duration (which may not affect much), and increases the address space pressure;

5) the practicality of exceptions may stimulate developers to throw exceptions when they are not appropriate, or recover from exceptions in unsafe places. For example, an exception may be thrown due to illegal user input. If exceptions are allowed, there will be a lot of such a programming style guide (Note by the translator, this is a little far-fetched :-()!

Conclusion:

On the surface, the advantage of using exceptions is greater than the disadvantage, especially in new projects. However, for existing code, the introduction of exceptions involves all dependent code. If exceptions are allowed to be used in a new project, it is also troublesome to integrate with code that has not previously used exceptions. Because most of Google's existing C ++ Code does not handle exceptions, it is quite difficult to introduce new code with exception handling.

Given that Google's existing code does not accept exceptions, using exceptions in existing code is much more costly than using exceptions in new projects. The migration process is slow and error-prone. We do not believe that an effective alternative to exceptions, such as error code and assertions, is a serious burden.

We do not oppose exceptions on the philosophical or moral level, but on the basis of practice. Because we want to use open-source projects on Google, it is inconvenient to use exceptions in projects, because we also recommend that you do not use exceptions in open-source projects on Google, if we need to reinvent these projects, it is not realistic.

For Windows code, this is an exception (wait until the last article: d ).

Note: exception handling is clearly not clear in just a few words. Taking constructor as an example, many c ++ books have mentioned that only exceptions can be processed when the constructor fails, google prohibits the use of exceptions. This is only for its convenience. To put it bluntly, it is only based on the software management cost. In actual use, it is determined by yourself.

 

7. Run-Time type information (rtti)

We disable rtti.

Definition: rtti allows programmers to recognize the types of C ++ class objects at runtime.

Advantages:

Rtti is useful in some unit tests, such as checking whether a new object is the expected dynamic type during a factory test.

In addition to testing, it is rarely used.

Disadvantage: identifying the type during runtime means there is a problem with the design itself. If you need to determine the type of an object during runtime, it usually means you need to reconsider the design of your class.

Conclusion:

In addition to unit tests, do not use rtti. If you find that the code you want to write varies with object types, consider recognizing object types in another way.

Virtual functions can execute different code with different subclass types. The work is done by the object itself.

If the work is completed in code other than the object, consider the dual-distribution scheme, such as the visitor mode, you can easily determine the type of the class outside the object itself.

If you don't think you can master the above method, you can use rtti, but be sure to think twice and do not implement a seemingly rtti solution (rtti-like workaround) manually. We are opposed to using rtti, likewise, it is opposed to the alternative scheme of seemingly class inheritance that is labeled with type labels. (You can use it if you don't use it or create a wheel: D ).

 

 

 

8. type conversion (casting)

Use static_cast <> () and other C ++ type conversion. Do not use int y = (INT) X or Int y = int (x );.

Definition: C ++ introduces different types of conversion operations different from C.

Advantage: The type conversion problem of C language is that the operation is vague: sometimes it is forced conversion (for example, (INT) 3.5), sometimes it is doing type conversion (such as (INT) "Hello "). In addition, the C ++ type conversion is easier and more eye-catching.

Disadvantage: The syntax is disgusting (nasty ).

Conclusion: C ++ style is used instead of C style conversion.

1) static_cast: similar to C-style conversion, it can be forced conversion of values, or explicit upward conversion from the parent class of the pointer to the subclass;

2) const_cast: remove the const attribute;

3) reinterpret_cast: unsafe mutual conversion between pointer types and integer or other pointers. It is only used when you are aware of everything you have done;

4) dynamic_cast: Do not use dynamic_cast unless it is a unit test. If you need to determine the type information during runtime, it indicates that the design is defective (refer to rtti ).

 

9. Stream (streams)

Use a stream only when logging.

Definition: stream is an alternative of printf () and scanf.

Advantage: With a stream, you do not need to care about the object type during output, and do not need to worry about the mismatch between the formatted string and the parameter list (although printf does not exist in GCC ), when a file is opened or closed, the stream can be automatically constructed and parsed.

Disadvantage: stream makes it difficult to execute functions such as pread (). If you do not use functions such as printf but use a stream, it is difficult to perform operations on the format (especially the commonly used format string %. * s). The Stream does not support the re-ordering of string operators (% 1 s), which is useful for internationalization.

Conclusion:

Do not use streams unless required by log interfaces.

There are many advantages and disadvantages of using a stream. Code consistency is better than anything else. Do not use a stream in code.

Extended discussion:

There are some arguments about this rule, and the underlying reasons are given here. The principle of uniqueness (only one way): We want to use only one fixed I/O type at any time to ensure that the Code is consistent across all I/O. Therefore, we do not want users to decide whether to use stream or printf + read/write. We should decide which method to use. Taking logs as an exception is because the stream is very suitable for this and has some historical reasons.

Stream supporters advocate that stream is the best choice, but their views are not so clear and powerful. They point out that all the advantages of stream are also the disadvantages. The biggest advantage of a stream is that you do not need to care about the type of the output object during output. This is a bright spot and a disadvantage: it is easy to use the error type, and the compiler will not trigger an alarm. When using a stream, the following errors are easily caused:

Cout <this; // prints the address

Cout <* This; // prints the contents

The compiler will not report an error, because <is overloaded, because we oppose the use of operator overloading.

Some people say that the formatting of printf is ugly and easy to use, but the stream is not good enough. Let's take a look at the following two pieces of code. Which one is easier to read?

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 may say, "It would be better to encapsulate the stream." Here is the case. What about other places? And don't forget, our goal is to make the language as small as possible, rather than adding new content that others need to learn.

Each method has its own advantages and disadvantages. "There is no best, only better". The simple dogma tells us that we must choose one of them. Most of the final decisions are printf + read/write.

 

 

 

10. preincrement and predecrement)

Prefix (++ I) Auto-increment and auto-increment operators are used for iterators and other template objects.

Definition: for a variable in the auto-increment (++ I or

When the value of the expression after UN "> I ++) or auto-subtraction (-- I or I --) is not used, you need to determine whether to use the pre-or post-auto-increment auto-subtraction.

Advantage: If the return value is not considered, the pre-auto-increment (++ I) is generally more efficient than the post-auto-increment (-- I, because the post auto-increment and auto-subtraction requires a copy of the expression value I, if I is an iterator or other non-numeric type, the copy cost is relatively large. Since the two auto-increment actions are the same (the Translator's note does not consider the value of the expression, I believe you know what I am talking about), why not directly use the pre-increment?

Disadvantage: in C language, when the expression value is not used, the traditional method is to use Post auto-increment, especially in the for loop. Some people think that post auto-increment is easier to understand, because it is similar to natural language, the subject (I) is before the predicate verb (++.

Conclusion: for simple values (non-objects), both of them do not matter. For the iterator and template types, use pre-auto-increment (auto-increment ).

 

11. Use of const)

We strongly recommend that you use const whenever possible.

Definition: add the keyword const before declared variables or parameters to indicate that the variable value cannot be modified (for example, const int Foo ), adding a const limitation to a function in the class indicates that the function will not modify the state of the class member variable (for example, class Foo {int bar (char c) const ;};).

Advantage: it is easier for people to understand how variables are used. The editor can better detect types and generate code. People are more confident in writing correct code because they know that the called function is limited to or cannot modify variable values. Even in lockless multi-threaded programming, people know what functions are safe.

Disadvantage: If you pass in a const variable to a function, the function prototype must also be const (otherwise, the variable must be converted to the const_cast type), which is especially troublesome when calling the library function.

Conclusion: const variables, data members, functions, and parameters provide an enhanced level of protection for type detection during compilation, so as to better detect errors as soon as possible. Therefore, we strongly recommend that you use const in any situations where you can:

1) if the function does not modify the input reference or pointer type parameters, such parameters should be const;

2) Declare the function as const as much as possible, and access the function should always be const. If other functions do not modify any data member, they should also be const. Do not call non-const functions, do not return non-const pointers or references to data members;

3) if the data member does not change after the object is constructed, it can be defined as Const.

However, do not over-use the const, such as const int * const X; it is a bit too much. Even if we write the exact description of X, in fact, you can write it as const int ** X.

The mutable keyword can be used, but it is not safe in multithreading. thread security should be considered first.

Const location:

Some people like the int const * Foo form and do not like the const int * Foo form. They think that the former is more consistent and therefore more readable: they follow the principle that const is always located after the object (INT) described by it. However, the consistency principle does not apply here, and the "do not over-use" authority offsets the consistent use. Putting const in front is easier to read, because in natural language, the adjective (const) is before the noun (INT.

This is to say, we advocate const first, not a requirement, but we must take into account the code consistency!

 

12. INTEGER (integer types)

In the C ++ built-in integer, int is used only. If the program needs a variable of different sizes, you can use <stdint. h> the integer of the precise width (precise-width) in, such as int16_t.

Definition: C ++ does not specify the integer size. Generally, people think that short is 16 bits, Int Is 32 bits, long is 32 bits, and long is 64 bits.

Advantage: maintain consistency of declarations.

Disadvantage: The integer size in c ++ varies with the compiler and architecture.

Conclusion:

<Stdint. h> defines integer types such as int16_t, uint32_t, and int64_t. You can use them to replace short, unsigned long, and so on to determine the size of integer types. In integer c, only int is used. We recommend that you use standard types, such as size_t and ptrdiff_t, as appropriate.

The most commonly used is that for integers, it is usually not used too large, such as cyclic counting. You can use a common Int. You can think that int is at least 32 bits, but do not think it will be more than 32 bits. If you need a 64-bit integer, you can use int64_t or uint64_t.

Int64_t is used for large integers.

Do not use unsigned integers such as uint32_t unless you are representing a bit pattern rather than a value. Even if the value is not a negative value, do not use the unsigned type. Use assertion, the Translator's note. This makes sense. The computer will only determine the positive and negative values based on variables, return values, and other symbols, still cannot be determined) to protect data.

Unsigned integer:

Some people, including some textbook authors, are recommended to use the unsigned type to indicate non-negative numbers, and the type indicates the value form. However, in C, this advantage is overwhelmed by the bugs caused by it. Let's see:

For (unsigned int I = Foo. Length ()-1; I> = 0; -- I )...

The above code will never be terminated! Sometimes GCC detects this bug and sends an alarm, but it usually does not. Similar bugs may also occur when there are variable compliance and unsigned variables, mainly the type-promotion scheme (Type-promotion scheme, in C language, the relationships between various built-in types are upgraded and converted. This will lead to unsigned type behavior beyond your expectation.

Therefore, use assertions to declare variables as non-negative values. Do not use the unsigned type.

 

 

 

Portability in 13.64 bits (64-bit portability)

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

1) Some types specified by printf () are not very portable on 32-bit and 64-bit systems, and c99 standards define some portable formats. Unfortunately, not all msvc 7.1 is supported, and some standards are missing. So sometimes we have to define ugly versions by ourselves (use the standard style to include 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 types) % LX % P  
Int64_t % Qd, % LLD % "Prid64"  
Uint64_t % Qu, % LlU, % LLX % "Priu64", % "prix64"  
Size_t % U % "Prius", % "Prixs" C99 specify % zu
Ptrdiff_t % D % "Prids" Specify % ZD for c99

Note that macro Pri * will be extended by the compiler as an independent string. Therefore, if you use a very large number of formatted strings, you need to insert the macro value instead of the macro name into the format, when using macro Pri *, you can specify the length after %. For example, printf ("x = % 30" Prius "N ",

X) will be extended to printf ("x = % 30" "U" "N", x) on 32-bit Linux ), the compiler processes it as printf ("x = % 30un", X ).

2) Remember sizeof (void *)! = Sizeof (INT). intptr_t is required if an integer of the pointer size is required.

3) Pay attention to structure alignment, especially for structures stored on disks. In a 64-bit system, any class/struct with int64_t/uint64_t members will be processed as 8-byte alignment by default. If the 32-bit and 64-bit code share the struct on the disk, you must ensure the alignment of the struct under the two architectures is consistent. Most compilers provide methods to adjust the structure alignment. In GCC, _ attribute _ (packed) can be used. msvc provides # pragma pack () and _ declspec (align, you can also directly set the project properties of the solution ).

4) when creating a 64-bit constant, use ll or ull as the suffix, for example:

Int64_t my_value = 0x123456789ll;

Uint64_t my_mask = 3ull <48;

5) if you really need 32-bit and 64-bit systems with different codes, you can use them before code variables. (Do not do this as much as possible, and modify the localization whenever possible during use ).

 

 

 

14. Pre-processing macro (Preprocessor macros)

Exercise caution when using macros, and replace them with unassociated functions, enumerations, and constants as much as possible.

Macros mean that the code you see is different from the code seen by the compiler, which may lead to abnormal behavior, especially when the macro is stored in the global scope.

Fortunately, in C ++, macros are not as necessary as in C. Performance-critical code can be replaced by inline functions, macro storage constants can be replaced by const variables, and macro long variable names can be referenced; compile with macro conditions. This ......, It is better not to do this, it will make the test more painful (# define prevents header file re-inclusion is of course an exception ).

Macros can do things that cannot be implemented by other technologies. In some code libraries (especially the underlying libraries), you can see some features of macros (such as stringifying, Translator note, use #), connection (concatenation, Translator's note, use #), and so on ). However, before using it, you must carefully consider whether the same effect can be achieved by using a macro.

Note: For more information about advanced macro applications, see advanced application of C macro.

The usage modes provided below can avoid macro usage issues for your reference:

1) do not define Macros in. H files;

2) correct before use # define, correct after use # UNDEF;

3) do not just use # UNDEF for an existing macro and select a name that does not conflict with each other;

4) if you do not use the macro that will lead to unstable C ++ constructs (unbalanced C ++ constructs, the Translator's note), at least the document will describe its behavior.

15.0 and null (0 and null)

The integer is 0, the real number is 0.0, the pointer is null, and the character (string) is'

 

 

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.