1# C++ Coding Style Guide 2 3## <a name="c0-1"></a>Purpose 4Rules are not perfect. They might disable useful features in specific situations and therefore affect code implementation. However, the purpose of developing rules is to get more benefits for most programmers. If a rule cannot be followed in your team operation, we can improve the rule together. 5Before referring to this coding style guide, you are expected to have the following basic capabilities of the C++programming language: 61. Have a general knowledge of ISO standards for C++. 72. Be familiar with the basic features of C++, including those of C++ 03/11/14/17. 83. Have a general knowledge of the C++ standard library. 9 10## <a name="c0-2"></a>General Principles 11Code must meet the requirements for readability, maintainability, security, reliability, testability, efficiency, and portability while ensuring functionality correctness. 12 13## <a name="c0-3"></a>Key Points 141. C++ programming style, such as naming and typesetting. 152. C++ modular design, including how to design header files, classes, interfaces, and functions. 163. Best practices of C++ features, including constants, type casting, resource management, and templates. 174. Best practices of modern C++, including conventions that can improve code maintainability and reliability in C++ 11/14/17. 185. This coding style guide is preferentially applicable to C++17. 19 20## <a name="c0-4"></a>Conventions 21**Rule**: Conventions that must be followed during programming. 22 23**Rec**: Conventions that must be considered during programming. 24 25This document is applicable to standard C++ versions (C++ 03/11/14/17) unless otherwise specified. 26 27## <a name="c0-5"></a>Exceptions 28It is necessary to understand the reason for these conventions and try to comply with them, no matter if they are rules or recommendations. 29However, some rules and recommendations have exceptions. 30 31The only acceptable exceptions are those that do not violate the general principles and provide appropriate reasons for the exception. 32Try to avoid exceptions because they affect the code consistency. Exceptions to 'Rules' should be very rare. 33 34The style consistency principle is preferred in the following case: 35When you modify open-source or third-party code, comply with their respective code specifications. 36 37# <a name="c2"></a>2 Naming 38## <a name="c2-1"></a>General Naming Rules 39__CamelCase__ 40CamelCase is the practice of writing compound words or phrases so that each word or abbreviation in the phrase begins with a capital letter, and with no intervening spaces or punctuation. 41There are two conventions: UpperCamelCase and lowerCamelCase. 42 43 44| Type | Naming Style | 45| ---------------------------------------- | ---------------------------------------- | 46| Class, struct, enumeration, union, scope name| UpperCamelCase | 47| Functions (including global functions, scope functions, and member functions) | UpperCamelCase | 48| Global variables (including variables of the global and namespace scopes, and class static variables), local variables, function parameters, and class, struct, and union member variables | lowerCamelCase | 49| Macro, constant, enumerated value, goto tag| All capitalized, separated by underscores (\_)| 50 51Note: 52**Constant** in the above table refers to the variable that is of the basic data type, enumeration type, and string type and modified by **const** or **constexpr** under the global scope, the namespace scope, and the scope of a static member of a class, excluding arrays and other variables. 53**Variable** indicates the variables excluding those defined in **Constant**. These variables use the lowerCamelCase style. 54 55## <a name="c2-2"></a>File Naming 56### <a name="r2-2-1"></a>Rule 2.2.1 Use .cpp as the C++ file name extension and .h as the header file name extension. 57It is recommended that you use .h as the file name extension of a header file so that the header file is compatible with C and C++. 58It is recommended that you use .cpp as the file name extension of an implementation file. In this way, you can easily distinguish C++ code from C code. 59 60At present, there are some other file name extensions used by programmers: 61 62- Header files: .hh, .hpp, .hxx 63- Implementation files: .cc, .cxx, .C 64 65If your project team uses a specific file name extension, you can continue to use it and keep the style consistent. 66This document uses .h and .cpp extensions. 67 68 69### <a name="r2-2-2"></a>Rule 2.2.2 Keep C++ file names the same as the class name. 70The names of the C++ header file and the C++ implementation file must be the same as the class name. Use the unix\_like style and keep the style consistent. 71 72For example, if there is a class named DatabaseConnection, the corresponding file names are as follows: 73- database_connection.h 74- database_connection.cpp 75 76The naming rules of struct, namespace, and enumeration definition files are similar to the rules above. 77 78## <a name="c2-3"></a>Function Naming 79Functions are named in the UpperCamelCase style. Generally, the verb or verb-object structure is used. 80```cpp 81class List { 82public: 83 void AddElement(const Element& element); 84 Element GetElement(const unsigned int index) const; 85 bool IsEmpty() const; 86}; 87 88namespace Utils { 89 void DeleteUser(); 90} 91``` 92 93## <a name="c2-4"></a>Type Naming 94 95Types are named in the UpperCamelCase style. 96All types, such as classes, structs, unions, typedefs, and enumerations, use the same conventions. Example: 97```cpp 98// Classes, structs, and unions 99class UrlTable { ... 100class UrlTableTester { ... 101struct UrlTableProperties { ... 102union Packet { ... 103 104// typedefs 105typedef std::map<std::string, UrlTableProperties*> PropertiesMap; 106 107// Enums 108enum UrlTableErrors { ... 109``` 110 111For namespace naming, UpperCamelCase is recommended. 112```cpp 113// Namespaces 114namespace OsUtils { 115 116namespace FileUtils { 117 118} 119 120} 121``` 122 123 124### <a name="a2-4-1"></a>Rec 2.4.1 Do not abuse typedef or #define to set alias for the basic data types. 125Unless otherwise specified, do not use typedef or #define to redefine basic data types. 126The basic data types found in the `<cstdint>` header file are preferable. 127 128| Signed Type | Unsigned Type | Description | 129| ----------- | ------------- | ---------------------------------------- | 130| int8_t | uint8_t | The signed or unsigned 8-bit integer type. | 131| int16_t | uint16_t | The signed or unsigned 16-bit integer type. | 132| int32_t | uint32_t | The signed or unsigned 32-bit integer type. | 133| int64_t | uint64_t | The signed or unsigned 64-bit integer type. | 134| intptr_t | uintptr_t | The signed or unsigned integer type large enough to hold a pointer. | 135 136 137## <a name="c2-5"></a>Variable Naming 138General variables, including global variables, function parameters, local variables, and member variables, are named in the lowerCamelCase style. 139```cpp 140std::string tableName; // Good: Recommended style. 141std::string tablename; // Bad: Forbidden style. 142std::string path; // Good: When there is only one word, lowerCamelCase (all lowercase) is used. 143``` 144 145### <a name="r2-5-1"></a>Rule 2.5.1 Add the prefix 'g_' to global variables. Do not add a prefix to a static variable. 146Global variables should be used as little as possible, and special attention should be paid to their use. This prefix highlights global variables so that developers can be more careful when handling them. 147- Global static variables and global variables are named in the same way. 148- Static variables in functions and common local variables are named in the same way. 149- Static member variables in classes and common member variables are named in the same way. 150 151```cpp 152int g_activeConnectCount; 153 154void Func() 155{ 156 static int packetCount = 0; 157 ... 158} 159``` 160 161### <a name="r2-5-2"></a>Rule 2.5.2 Name member variables in classes in the unix\_like style. 162 163```cpp 164class Foo { 165private: 166 std::string fileName_; // Add the underscore (\_) as the suffix, similar to the K&R naming style. 167}; 168``` 169Use the lowerCamelCase style and do not add prefixes or suffixes to name a member variable of the struct or union type. Keep the naming style consistent with that for a local variable. 170 171## <a name="c2-6"></a>Macro, Constant, and Enum Naming 172Use uppercase letters separated by underscores (\_) for macro names and enumerated values. 173In the global scope, constants of named and unnamed namespaces and static member constants should be capitalized and separated with underscores (\_).Local constants and ordinary const member variables use the lowerCamelCase naming style. 174 175```cpp 176#define MAX(a, b) (((a) < (b))? (b): (a)) // Example of naming a macro only. 177 178enum BaseColor { // Note: The enum type name is in the UpperCamelCase style, whereas the enumerated value is in uppercase letters separated by underscores (\_). 179 RED, 180 DARK_RED, 181 GREEN, 182 LIGHT_GREEN 183}; 184 185int Func(...) 186{ 187 const unsigned int bufferSize = 100; // Local variable 188 char *p = new char[bufferSize]; 189 ... 190} 191 192namespace Utils { 193 const unsigned int DEFAULT_FILE_SIZE_KB = 200; // Global variable 194} 195 196``` 197 198# <a name="c3"></a>3 Formatting 199 200## <a name="c3-1"></a>Line Length 201 202### <a name="r3-1-1"></a>Rule 3.1.1 Include 120 characters or less in each line. 203If the line of code exceeds the permitted length, wrap the line appropriately. 204 205Exceptions: 206- If a line of comment contains a command or URL of more than 120 characters, you can keep the line for easy copy, paste, and search using the grep command. 207- The #include and #error statements are allowed to exceed the line length requirement. However, you should try to avoid this. 208- The error information in preprocessor directives can exceed the permitted length. 209 Put the error information of preprocessor directives in one line to facilitate reading and understanding even if the line contains more than 120 characters. 210```cpp 211#ifndef XXX_YYY_ZZZ 212#error Header aaaa/bbbb/cccc/abc.h must only be included after xxxx/yyyy/zzzz/xyz.h, because xxxxxxxxxxxxxxxxxxxxxxxxxxxxx 213#endif 214``` 215 216## <a name="c3-2"></a>Indentation 217 218### <a name="r3-2-1"></a>Rule 3.2.1 Use spaces to indent and indent 4 spaces at a time. 219Only spaces can be used for indentation. Four spaces are indented each time. Do not use the Tab character to indent. 220Currently, almost all integrated development environments (IDEs) support automatic conversion of a Tab input to four spaces. Configure your IDE to support indentation with spaces. 221 222## <a name="c3-3"></a>Braces 223### <a name="r3-3-1"></a>Rule 3.3.1 Use the K&R indentation style. 224__K&R style__ 225While wrapping a line, the left brace of the function (excluding the lambda statement) starts a new line and takes a single line. Other left braces are placed at the end of the line along with the statement. 226The right brace takes a single line, unless it is followed by the rest of the same statement, such as `while` in the `do` statement, `else` or `else if` in the `if` statement, a comma, or a semicolon. 227 228Example: 229```cpp 230struct MyType { // The left brace is placed at the end of the line along with the statement, and one space is used for indentation. 231 ... 232}; 233 234int Foo(int a) 235{ // The left brace of the function starts a new line, and nothing else is placed on the line. 236 if (...) { 237 ... 238 } else { 239 ... 240 } 241} 242``` 243The reasons for recommending this style are as follows: 244 245- Code is more compact. 246- Placing the brace at the end of the statement makes the code more continuous in reading rhythm than starting a new line. 247- This style complies with mainstream norms and habits of programming languages. 248- Most modern IDEs have an automatic code indentation, alignment and display. Placing the brace at the end of a line does not impact understanding. 249 250 251If no function body is inside the braces, the braces can be put on the same line. 252```cpp 253class MyClass { 254public: 255 MyClass() : value_(0) {} 256 257private: 258 int value_; 259}; 260``` 261 262## <a name="c3-4"></a>Function Declarations and Definitions 263 264### <a name="r3-4-1"></a>Rule 3.4.1 Keep the return type and function name of the function declaration or definition in the same line, and align the function parameter list appropriately if it needs to be wrapped. 265When a function is declared and defined, the return value type of the function should be in the same line as the function name. When the function parameter list is wrapped, it should be aligned appropriately. 266The left parenthesis of a parameter list is always in the same line as the function name. The right parenthesis always follows the last parameter. 267 268Example: 269```cpp 270ReturnType FunctionName(ArgType paramName1, ArgType paramName2) // Good: All are in the same line. 271{ 272 ... 273} 274 275ReturnType VeryVeryVeryLongFunctionName(ArgType paramName1, // Each added parameter starts on a new line because the line length limit is exceeded. 276 ArgType paramName2, // Good: aligned with the previous parameter. 277 ArgType paramName3) 278{ 279 ... 280} 281 282ReturnType LongFunctionName(ArgType paramName1, ArgType paramName2, // Parameters are wrapped because the line length limit is exceeded. 283 ArgType paramName3, ArgType paramName4, ArgType paramName5) // Good: After the line break, 4 spaces are used for indentation. 284{ 285 ... 286} 287 288ReturnType ReallyReallyReallyReallyLongFunctionName( // The line length cannot accommodate even the first parameter, and a line break is required. 289 ArgType paramName1, ArgType paramName2, ArgType paramName3) // Good: After the line break, 4 spaces are used for indentation. 290{ 291 ... 292} 293``` 294 295## <a name="c3-5"></a>Function Calls 296### <a name="r3-5-1"></a>Rule 3.5.1 A function call parameter list should be placed on one line. When the parameter list exceeds the line length and requires a line break, the parameters should be properly aligned. 297A function call parameter list should be placed on one line. When the parameter list exceeds the line length and requires a line break, the parameters should be properly aligned. 298The left parenthesis always follows the function name, and the right parenthesis always follows the last parameter. 299 300The following are examples of proper line breaks: 301```cpp 302ReturnType result = FunctionName(paramName1, paramName2); // Good: All function parameters are on one line. 303 304ReturnType result = FunctionName(paramName1, 305 paramName2, // Good: aligned with the previous parameter 306 paramName3); 307 308ReturnType result = FunctionName(paramName1, paramName2, 309 paramName3, paramName4, paramName5); // Good: Parameters are wrapped. After the line break, 4 spaces are used for indentation. 310 311ReturnType result = VeryVeryVeryLongFunctionName( // The line length cannot accommodate even the first parameter, and a line break is required. 312 paramName1, paramName2, paramName3); // After the line break, 4 spaces are used for indentation. 313``` 314 315If some of the parameters called by a function are associated with each other, you can group them for better understanding. 316```cpp 317// Good: The parameters in each line represent a group of data structures with strong correlation. They are placed on a line for ease of understanding. 318int result = DealWithStructureLikeParams(left.x, left.y, // A group of related parameters. 319 right.x, right.y); // Another group of related parameters. 320``` 321 322## <a name="c3-6"></a> if Statements 323 324### <a name="r3-6-1"></a>Rule 3.6.1 Use braces to include an if statement. 325We require that all if statements use braces, even if there is only one statement. 326 327Reasons: 328- The logic is intuitive and easy to read. 329- It is less prone to mistakes when new code is added to the existing if statement. 330- If function-like macros are used in a conditional statement, it is less prone to mistakes (in case the braces are missing when macros are defined). 331 332```cpp 333if (objectIsNotExist) { // Good: Braces are added to a single-line conditional statement. 334 return CreateNewObject(); 335} 336``` 337### <a name="r3-6-2"></a>Rule 3.6.2 Place if, else, and else if keywords on separate lines. 338If there are multiple branches in a conditional statement, they should be placed on separate lines. 339 340Good example: 341 342```cpp 343if (someConditions) { 344 DoSomething(); 345 ... 346} else { // Good: Put the if and else keywords on separate lines. 347 ... 348} 349``` 350 351Bad example: 352 353```cpp 354if (someConditions) { ... } else { ... } // Bad: The if and else keywords are put on the same line. 355``` 356 357## <a name="c3-7"></a> Loop Statements 358### <a name="r3-7-1"></a>Rule 3.7.1 Use braces after loop statements. 359Similar to if statements, we require that the for and while loop statements contain braces, even if the loop body is empty or there is only one loop statement. 360```cpp 361for (int i = 0; i < someRange; i++) { // Good: Braces are used. 362 DoSomething(); 363} 364``` 365```cpp 366while (condition) {} // Good: The while loop body is empty. Braces should be used. 367``` 368```cpp 369while (condition) { 370 continue; // Good: The continue keyword highlights the end of the empty loop. Braces should be used. 371} 372``` 373 374Bad example: 375```cpp 376for (int i = 0; i < someRange; i++) 377 DoSomething(); // Bad: Braces are mandatory. 378``` 379```cpp 380while (someCondition) ; // Bad: Using a semicolon here will make people misunderstand that it is a part of the while statement and not the end to it. 381``` 382 383## <a name="c3-8"></a> Switch Statements 384### <a name="r3-8-1"></a>Rule 3.8.1 Indent case and default in a switch statement with four spaces. 385The indentation style of the switch statement is as follows: 386```cpp 387switch (var) { 388 case 0: // Good: Indented 389 DoSomething1(); // Good: Indented 390 break; 391 case 1: { // Good: Braces are added. 392 DoSomething2(); 393 break; 394 } 395 default: 396 break; 397} 398``` 399 400```cpp 401switch (var) { 402case 0: // Bad: case is not indented. 403 DoSomething(); 404 break; 405default: // Bad: default is not indented. 406 break; 407} 408``` 409 410## <a name="c3-9"></a> Expressions 411 412### <a name="a3-9-1"></a>Rec 3.9.1 Keep a consistent line break style for expressions and ensure that operators are placed at the end of a line. 413A long expression that does not meet the line length requirement must be wrapped appropriately. Generally, the expression is wrapped at an operator of a lower priority or a connector, and the operator or connector is placed at the end of the line. 414Placing these at the end of a line indicates that the operation is to be continued on the next line. 415Example: 416 417// Assume that the first line exceeds the length limit. 418```cpp 419if (currentValue > threshold && // Good: After the line break, the logical-AND operators are placed at the end of the line. 420 someCondition) { 421 DoSomething(); 422 ... 423} 424 425int result = reallyReallyLongVariableName1 + // Good 426 reallyReallyLongVariableName2; 427``` 428After an expression is wrapped, ensure that the lines are aligned appropriately or indented with 4 spaces. See the following example. 429 430```cpp 431int sum = longVariableName1 + longVariableName2 + longVariableName3 + 432 longVariableName4 + longVariableName5 + longVariableName6; // Good: indented with 4 spaces 433 434int sum = longVariableName1 + longVariableName2 + longVariableName3 + 435 longVariableName4 + longVariableName5 + longVariableName6; // Good: The lines are aligned. 436``` 437## <a name="c3-10"></a> Variable Assignment 438 439### <a name="r3-10-1"></a>Rule 3.10.1 Multiple variable definitions and assignment statements cannot be written on one line. 440Each line should have only one variable initialization statement. It is easier to read and understand. 441 442```cpp 443int maxCount = 10; 444bool isCompleted = false; 445``` 446 447Bad example: 448 449```cpp 450int maxCount = 10; bool isCompleted = false; // Bad: Multiple variable initialization statements must be separated on different lines. Each variable initialization statement occupies one line. 451int x, y = 0; // Bad: Multiple variable definitions must be separated on different lines. Each definition occupies one line. 452 453int pointX; 454int pointY; 455... 456pointX = 1; pointY = 2; // Bad: Multiple variable assignment statements must be separated on different lines. 457``` 458Exception: Multiple variables can be declared and initialized in the for loop header, if initialization statement (C++17), and structured binding statement (C++17). Multiple variable declarations in these statements have strong associations. Forcible division into multiple lines may cause problems such as scope inconsistency and separation of declaration from initialization. 459 460## <a name="c3-11"></a> Initialization 461Initialization is applicable to structs, unions, and arrays. 462 463### <a name="r3-11-1"></a>Rule 3.11.1 When an initialization list is wrapped, ensure that the line after the break is indented and aligned properly. 464If a structure or array initialization list is wrapped, the line after the break is indented with four spaces. 465Choose the wrap location and alignment style for best comprehension. 466 467```cpp 468const int rank[] = { 469 16, 16, 16, 16, 32, 32, 32, 32, 470 64, 64, 64, 64, 32, 32, 32, 32 471}; 472``` 473 474## <a name="c3-12"></a> Pointers and References 475### <a name="a3-12-1"></a>Rec 3.12.1 The pointer type `*` follows a variable name or type. There can be only one space to the side of it. 476Pointer naming: There can be only one space next to `*`. 477```cpp 478int* p = NULL; // Good 479int *p = NULL; // Good 480 481int*p = NULL; // Bad 482int * p = NULL; // Bad 483``` 484 485Exception: When a variable is modified by const or restrict, `*` cannot follow the variable or type. 486```cpp 487const char * const VERSION = "V100"; 488``` 489 490### <a name="a3-12-2"></a>Rec 3.12.2 The reference type `&` follows a variable name or type. There can be only one space to the side of it. 491Reference naming: There can be only one space around `&`. 492```cpp 493int i = 8; 494 495int& p = i; // Good 496int &p = i; // Good 497int*& rp = pi; // Good: The reference type `*&` follows the type. 498int *&rp = pi; // Good: The reference type `*&` follows the variable name. 499int* &rp = pi; // Good: The pointer type `*` follows the type and the eference type `&` follows the variable name. 500 501int & p = i; // Bad 502int&p = i; // Bad 503``` 504 505## <a name="c3-13"></a>Compilation Preprocessing 506### <a name="r3-13-1"></a>Rule 3.13.1 Place a number sign (#) at the beginning of a line for compilation preprocessing. In nested compilation preprocessing, the number sign (#) can be indented. 507The number sign (#) must be placed at the beginning of a line for compilation preprocessing, even if the code is embedded in the function body. 508 509### <a name="r3-13-2"></a>Rule 3.13.2 Do not use macros. 510Macros do not obey scope, type system, and other rules, and may easily lead to issues. Avoid macro definitions wherever possible. If you must use macros, give them unique names. 511In C++, there are many ways to avoid using macros: 512- Use `const` or `enum` to define constants that are easy to understand. 513- Use namespaces to avoid name conflicts. 514- Use the `inline` function to avoid function call overhead. 515- Use the `template` function to handle multiple types. 516Macros can be used in scenarios such as header guards, conditional compilation, and logging. 517 518### <a name="r3-13-3"></a>Rule 3.13.3 Do not use macros to represent constants. 519Macros involve simple text replacement, which is completed during preprocessing. When an error occurs, the macro value is reported without the macro name. During tracing and debugging, the macro name is not displayed either. Besides, macros do not have type checking or scopes. 520 521### <a name="r3-13-4"></a>Rule 3.13.4 Do not use function-like macros. 522Before defining a function-like macro, consider whether it can be replaced with a function. If yes, you are advised to use a function for replacement. 523The disadvantages of the function-like macro are as follows: 524- Function-like macros have no type check, which is not as strict as the function call check. 525- If macro parameters are not evaluated during macro expansion, unexpected results may occur. 526- A macro has no independent scope. 527- There are high skill requirements on the proper use of macros (for example, the usage of `#` and wide use of parentheses), which reduces readability. 528- Extensions of some macros can only be implemented by specific compilers in specific scenarios, such as `statement expression` of `gcc`, reducing the portability. 529- After the macro is expanded during precompilation, it is invisible during subsequent compilation, linking, and debugging. Besides, macros that contain multiple lines are expanded into a line. Function-like macros are difficult to debug, set breakpoints, and locate in case of bugs. 530- Macros containing a large number of statements must be expanded at each call point. If there are many call points, the code will be expanded. 531 532Unlike macros, functions do not have these disadvantages. However, the biggest disadvantage of functions is low execution efficiency (increasing the overhead of function calls and the difficulty of compiler optimization). 533In light of this, you can use inline functions when necessary. Similar to macros, inline functions are expanded at the call point. The difference is that inline functions are expanded during compilation. 534 535Inline functions have the advantages of both functions and macros: 536- Strict type checking is performed for inline functions. 537- Parameters are evaluated only once for inline functions. 538- Inline functions are expanded in place and there is no overhead for function calls. 539- Inline functions are better optimized than standard functions. 540For performance-sensitive code, consider using inline functions instead of standard functions. 541 542Exceptions: 543In logging scenarios, only function-like macros can be used to keep information such as the file name (__FILE__) and line number (__LINE__) of the call point. 544 545## <a name="c3-14"></a> Whitespace 546### <a name="r3-14-1"></a>Rule 3.14.1 Ensure that horizontal spaces are used to highlight keywords and important information, and avoid unnecessary whitespace. 547Horizontal spaces are used to highlight keywords and important information. Spaces are not allowed at the end of each code line. The general rules are as follows: 548 549- Add spaces after keywords such as if, switch, case, do, while, and for. 550- Do not add spaces after the left parenthesis or before the right parenthesis. 551- For expressions enclosed by braces, either add a space on either side or avoid a space on either side. 552- Do not add spaces after any unary operator (& * + - ~ !). 553- Add a space to the left and right sides of each binary operator (= + -< > * /% | & ^ <= >= == !=). 554- Add spaces to the left and right sides of a ternary operator (? :). 555- Do not add spaces between a prefix or suffix increment (++) or decrement (--) operator and a variable. 556- Do not add spaces before or after a struct member operator (. ->). 557- Do not add spaces before commas. Add spaces after commas. 558- Do not add spaces between a template or type conversion operator (<>) and a type. 559- Do not add spaces before or after a domain operator (::). 560- Determine whether to add spaces before and after a colon (:) based on the actual situation. 561 562In normal cases: 563```cpp 564void Foo(int b) { // Good: A space is added before the left brace. 565 566int i = 0; // Good: During variable initialization, there should be spaces before and after =. Do not leave a space before the semicolon. 567 568int buf[BUF_SIZE] = {0}; // Good: Spaces are not added in braces. 569``` 570 571Function definition and call: 572```cpp 573int result = Foo(arg1,arg2); 574 ^ // Bad: A space should be added after the comma. 575 576int result = Foo( arg1, arg2 ); 577 ^ ^ // Bad: Spaces should not be added after the left parenthesis or before the right parenthesis. 578``` 579 580Pointer and Address Operator 581```cpp 582x = *p; // Good: There is no space between the operator * and the pointer p. 583p = &x; // Good: There is no space between the operator & and the variable x. 584x = r.y; // Good: When a member variable is accessed through the operator (.), no space is added. 585x = r->y; // Good: When a member variable is accessed through the operator (->), no space is added. 586``` 587 588Other Operators: 589```cpp 590x = 0; // Good: There is a space before and after the assignment operator (=). 591x = -5; // Good: There is no space between the minus sign (–) and the number. 592++x; //Good: Do not add spaces between a prefix or suffix increment (++) or decrement (--) operator and a variable.. 593x--; 594 595if (x && !y) // Good: There is a space before and after the Boolean operator. There is no space between the ! operator and the variable. 596v = w * x + y / z; // Good: There is a space before and after the binary operator. 597v = w * (x + z); // Good: There is no space before or after the expression in the parentheses. 598 599int a = (x < y) ? x : y; // Good: Ternary operator. There is a space before and after ? and : 600``` 601 602Loops and Conditional Statements: 603```cpp 604if (condition) { // Good: There is a space between the if keyword and the left parenthesis, and no space before or after the conditional statement in the parentheses. 605 ... 606} else { // Good: There is a space between the else keyword and the left brace. 607 ... 608} 609 610while (conditions) {} // Good: There is a space between the while keyword and the left parenthesis. There is no space before or after the conditional statement in the parentheses. 611 612for (int i = 0; i < someRange; ++i) { // Good: There is a space between the for keyword and the left parenthesis, and after the semicolon. 613 ... 614} 615 616switch (condition) { // Good: There is a space after the switch keyword. 617 case 0: // Good: There is no space between the case condition and the colon. 618 ... 619 break; 620 ... 621 default: 622 ... 623 break; 624} 625``` 626 627Templates and Conversions 628```cpp 629// Angle brackets (< and >) are not adjacent to space. There is no space before < or between > and (. 630vector<string> x; 631y = static_cast<char*>(x); 632 633// There can be a space between the type and the pointer operator. Keep the spacing style consistent. 634vector<char *> x; 635``` 636 637Scope Operators 638```cpp 639std::cout; // Good: Namespace access. Do not leave spaces. 640 641int MyClass::GetValue() const {} // Good: Do not leave spaces in the definition of member functions. 642``` 643 644Colons 645```cpp 646// Scenarios when space is required 647 648// Good: // Add a space before or after the colon in a derived class definition. 649class Sub : public Base { 650 651}; 652 653// Add a space before or after the colon in the initialization list of a constructor function. 654MyClass::MyClass(int var) : someVar_(var) 655{ 656 DoSomething(); 657} 658 659// Add a space before or after the colon in a bit-field. 660struct XX { 661 char a : 4; 662 char b : 5; 663 char c : 4; 664}; 665``` 666 667```cpp 668// Scenarios when space is not required 669 670// Good: // No space is added before or after the colon next to a class access permission (public or private). 671class MyClass { 672public: 673 MyClass(int var); 674private: 675 int someVar_; 676}; 677 678// No space is added before or after the colon in a switch statement. 679switch (value) 680{ 681 case 1: 682 DoSomething(); 683 break; 684 default: 685 break; 686} 687``` 688 689Note: Currently, all IDEs support automatic deletion of spaces at the end of a line. Please configure your IDE correctly. 690 691### <a name="a3-14-1"></a>Rec 3.14.1 Use blank lines only when necessary to keep code compact. 692 693There must be as few blank lines as possible so that more code can be displayed for easy reading. Recommendations: 694- Add blank lines according to the correlation between lines. 695- Consecutive blank lines are not allowed inside functions, type definitions, macros, and initialization expressions. 696- A maximum of **two** consecutive blank lines can be used. 697- Do not add blank lines on the first and last lines of a code block in braces. This recommendation is not applicable to code block in braces of a namespace. 698 699```cpp 700int Foo() 701{ 702 ... 703} 704 705 706 707int bar() {// Bad: More than one blank lines are used between two function definitions. 708{ 709 ... 710} 711 712 713if (...) { 714 // Bad: Do not add blank lines on the first and last lines of a code block. 715 ... 716 // Bad: Do not add blank lines on the first and last lines of a code block. 717} 718 719int Foo(...) 720{ 721 // Bad: Do not add blank lines before the first statement in a function body. 722 ... 723} 724``` 725 726## <a name="c3-15"></a> Classes 727### <a name="r3-15-1"></a>Rule 3.15.1 Class access specifier declarations are in the sequence: public, protected, private. Indent these specifiers to the same level as the class keyword. 728```cpp 729class MyClass : public BaseClass { 730public: // Not indented. 731 MyClass(); // Indented with 4 spaces. 732 explicit MyClass(int var); 733 ~MyClass() {} 734 735 void SomeFunction(); 736 void SomeFunctionThatDoesNothing() 737 { 738 } 739 740 void SetVar(int var) { someVar_ = var; } 741 int GetVar() const { return someVar_; } 742 743private: 744 bool SomeInternalFunction(); 745 746 int someVar_; 747 int someOtherVar_; 748}; 749``` 750 751In each part, it is recommended that similar statements be put together and in the following order: Type (including typedef, using, nested structs and classes), Constant, Factory Function, Constructor, Assignment Operator, Destructor, Other Member Function, and Data Member 752 753 754### <a name="r3-15-2"></a>Rule 3.15.2 The constructor initialization list must be on the same line or wrapped and aligned with four spaces of indentation. 755```cpp 756// If all variables can be placed on the same line 757MyClass::MyClass(int var) : someVar_(var) 758{ 759 DoSomething(); 760} 761 762// If the variables cannot be placed on the same line 763// Wrapped at the colon and indented with four spaces 764MyClass::MyClass(int var) 765 : someVar_(var), someOtherVar_(var + 1) // Good: Add a space after the comma. 766{ 767 DoSomething(); 768} 769 770// If an initialization list needs to be placed in multiple lines, put each member on a separate line and align between lines. 771MyClass::MyClass(int var) 772 someVar(var), // Four spaces of indentation. 773 someOtherVar_(var + 1) 774{ 775 DoSomething(); 776} 777``` 778 779# <a name="c4"></a>4 Comments 780Generally, clear architecture and good naming are recommended to improve code readability, and comments are provided only when necessary. 781Comments are used to help readers quickly understand code. Therefore, **comments should be provided for the sake of readers**. 782 783Comments must be concise, clear, and unambiguous, ensuring that information is complete and not redundant. 784 785**Comments are as important as code**. 786When writing a comment, you need to step into the reader's shoes and use comments to express what the reader really needs. Comments are used to express the function and intention of code, rather than repeating code. 787When modifying the code, ensure that the comments are consistent with the modified code. It is not polite to modify only code and keep the old comments, which will undermine the consistency between code and comments, and may confuse or even mislead readers. 788 789Comments should be made in English. 790 791## <a name="c4-1"></a>Comment Style 792 793In C++ code, both ` /* */` and ` // ` can be used for commenting. 794Comments can be classified into different types, such as file header comments, function header comments, and code comments. This is based on their purposes and positions. 795Comments of the same type must keep a consistent style. 796 797Note: Example code in this document uses comments in the `//` format only to better describe the rules and recommendations. This does not mean this comment format is better. 798 799## <a name="c4-2"></a> File Header Comments 800### <a name="r4-2-1"></a>Rule 4.2.1 File header comments must contain the copyright notice. 801 802/* 803 * Copyright (c) 2020 Huawei Device Co., Ltd. 804 * Licensed under the Apache License, Version 2.0 (the "License"); 805 * you may not use this file except in compliance with the License. 806 * You may obtain a copy of the License at 807 * 808 * http://www.apache.org/licenses/LICENSE-2.0 809 * 810 * Unless required by applicable law or agreed to in writing, software 811 * distributed under the License is distributed on an "AS IS" BASIS, 812 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 813 * See the License for the specific language governing permissions and 814 * limitations under the License. 815 */ 816 817## <a name="c4-3"></a> Function Header Comments 818### <a name="r4-3-1"></a>Rule 4.3.1 Write function header comments for public functions. 819Public functions are interfaces provided by classes for external systems. To use public functions, the caller must understand the functionalities, parameter value ranges, return values, and precautions of the functions. 820Write function header comments for the function value range, return value, and precautions, since they cannot be self-explained. 821 822### <a name="r4-3-2"></a>Rule 4.3.2 Function header comments with no content are forbidden. 823Not all functions need function header comments. 824For information that cannot be described by function signatures, add function header comments. 825 826Function header comments are placed above the function declaration or definition. Use one of the following styles: 827Use '//' to start the function header. 828 829```cpp 830// Single-line function header 831int Func1(void); 832 833// Multi-line function header 834// Second line 835int Func2(void); 836``` 837 838Use `/* */` to start the function header. 839```cpp 840/* Single-line function header */ 841int Func1(void); 842 843/* 844 * Another single-line function header 845 */ 846int Func2(void); 847 848/* 849 * Multi-line function header 850 * Second line 851 */ 852int Func3(void); 853``` 854Use function names to describe functions, and add function header comments if necessary. 855Do not write useless or redundant function headers. Do not write empty function headers with no content. 856 857The function header comment content will depend on the function and includes but is not limited to: a function description, return value, performance constraints, usage comments, memory conventions, algorithm implementation, reentering requirements. 858In the function interface declaration in the external header file, the function header comment should clearly describe important and useful information. 859 860Good example: 861 862```cpp 863/* 864 * The number of written bytes is returned. If -1 is returned, the write operation failed. 865 * Note that the memory buffer should be released by the caller. 866 */ 867int WriteString(const char *buf, int len); 868``` 869 870Bad example: 871```cpp 872/* 873 * Function name: WriteString 874 * Function: Write a character string. 875 * Parameters: 876 * Return value: 877 */ 878int WriteString(const char *buf, int len); 879``` 880Problems: 881 882- The 'Parameters' and 'Return value' have no content. 883- The function name is redundant. 884- The most import thing, that is, who needs to release the buffer, is not clearly stated. 885 886## <a name="c4-4"></a> Code Comments 887### <a name="r4-4-1"></a>Rule 4.4.1 Code comments are placed above or on the right of the corresponding code. 888### <a name="r4-4-2"></a>Rule 4.4.2 There must be a space between the comment character and the comment content. At least one space is required between the comment and code if the comment is placed to the right of code. 889Comments placed above code should be indented the same as that of the code. 890Use one of the following styles: 891Use "//". 892```cpp 893 894// Single-line comment 895DoSomething(); 896 897// Multi-line comment 898// Second line 899DoSomething(); 900``` 901 902Use `/* */`. 903```cpp 904/* Single-line comment */ 905DoSomething(); 906 907/* 908 * Multi-line comment in another mode 909 * Second line 910 */ 911DoSomething(); 912``` 913Leave at least one space between the code and the comment on the right. It is recommended that no more than four spaces be left. 914You can use the Tab key to indent 1–4 spaces. 915 916Select and use one of the following styles: 917 918```cpp 919int foo = 100; // Comment on the right 920int bar = 200; /* Comment on the right */ 921``` 922It is more appealing sometimes when the comment is placed on the right of code and the comments and code are aligned vertically. 923After the alignment, ensure that the comment is 1–4 spaces away from the widest line of code. 924Example: 925 926```cpp 927const int A_CONST = 100; /* Related comments of the same type can be aligned vertically. */ 928const int ANOTHER_CONST = 200; /* Leave spaces after code to align comments vertically. */ 929``` 930When the comment on the right exceeds the line width, consider placing the comment above the code. 931 932### <a name="r4-4-3"></a>Rule 4.4.3 Delete unused code segments. Do not comment them out. 933Code that is commented out cannot be maintained. If you attempt to restore the code, it is very likely to introduce ignorable defects. 934The correct method is to delete unnecessary code directly. If necessary, consider porting or rewriting the code. 935 936Here, commenting out refers to the removal of code from compilation without actually deleting it. This is done by using /* */, //, #if 0, #ifdef NEVER_DEFINED, and so on. 937 938### <a name="a4-4-1"></a>Rec 4.4.1 Delivered code cannot contain a TODO/TBD/FIXME comment. 939TODO/TBD comments are used to describe required improvements and supplements. 940FIXME comments are used to describe defects that need fixing. 941They should have a standardized style, which facilitates text search. Example: 942 943```cpp 944// TODO(<author-name>): XX 945// FIXME: XX 946``` 947 948 949# <a name="c5"></a>5 Header Files 950## <a name="c5-1"></a> Header File Responsibility 951A header file is an external interface of a module or file. The design of a header file shows most of the system design. 952The interface declaration for most functions is more suitable placed in the header file, but implementation (except inline functions) cannot be placed in the header file. Functions, macros, enumerations, and structure definitions that need to be used in .cpp files cannot be placed in the header file. 953The header responsibility should be simple. An overly complex header file will make dependencies complex and cause long compilation times. 954 955### <a name="a5-1-1"></a>Rec 5.1.1 Each .cpp file should have a .h file with the same name. It should be used to declare the classes and interfaces that need to be exposed externally. 956Generally, each .cpp file has a corresponding .h file. This .cpp file is used to store the function declarations, macro definitions, and class definitions that are to be exposed. 957If a .cpp file does not need to open any interface externally, it should not exist. 958Exception: __An entry point (for example, the file where the main function is located), unit tests, and dynamic libraries __ 959 960Example: 961```cpp 962// Foo.h 963 964#ifndef FOO_H 965#define FOO_H 966 967class Foo { 968public: 969 Foo(); 970 void Fun(); 971 972private: 973 int value_; 974}; 975 976#endif 977``` 978 979```cpp 980// Foo.cpp 981#include "Foo.h" 982 983namespace { // Good: The declaration of the internal function is placed in the header of the .cpp file, and has been limited to the unnamed namespace or static scope. 984 void Bar() 985 { 986 } 987} 988 989... 990 991void Foo::Fun() 992{ 993 Bar(); 994} 995``` 996 997## <a name="c5-2"></a> Header File Dependency 998### <a name="r5-2-1"></a>Rule 5.2.1 Header file cyclic dependency is forbidden. 999An example of cyclic dependency (also known as circular dependency) is: a.h contains b.h, b.h contains c.h, and c.h contains a.h. If any of these header files is modified, all code containing a.h, b.h, and c.h needs to be recompiled. 1000For a unidirectional dependency, for example if: a.h contains b.h, b.h contains c.h, and c.h does not contain any header file, modifying a.h does not mean that we need to recompile the source code for b.h or c.h. 1001 1002The cyclic dependency of header files reflects an obviously unreasonable architecture design, which can be avoided through optimization. 1003 1004 1005### <a name="r5-2-2"></a>Rule 5.2.2 Header files should have #define guards to prevent multiple inclusion. 1006To prevent header files from being included multiple times, all header files should be protected by #define. Do not use #pragma once. 1007 1008When defining a protection character, comply with the following rules: 10091) The protection character uses a unique name. 10102) Do not place code or comments (except for file header comments) before or after the protected part. 1011 1012Example: Assume that the timer.h file of the timer module is in the timer/include/timer.h directory. Perform the following operations to safeguard the timer.h file: 1013 1014```cpp 1015#ifndef TIMER_INCLUDE_TIMER_H 1016#define TIMER_INCLUDE_TIMER_H 1017... 1018#endif 1019``` 1020 1021### <a name="r5-2-3"></a>Rule 5.2.3 It is prohibited to reference external function interfaces and variables in extern declaration mode. 1022Interfaces provided by other modules or files can be used only by including header files. 1023Using external function interfaces and variables in extern declaration mode may cause inconsistency between declarations and definitions when external interfaces are changed. 1024In addition, this kind of implicit dependency may cause architecture corruption. 1025 1026Cases that do not comply with specifications: 1027 1028// a.cpp content 1029```cpp 1030extern int Fun(); // Bad: Use external functions in extern mode. 1031 1032void Bar() 1033{ 1034 int i = Fun(); 1035 ... 1036} 1037``` 1038 1039// b.cpp content 1040```cpp 1041int Fun() 1042{ 1043 // Do something 1044} 1045``` 1046Should be changed to: 1047 1048// a.cpp content 1049```cpp 1050#include "b.h" // Good: Use the interface provided by other .cpp by including its corresponding header file. 1051 1052void Bar() 1053{ 1054 int i = Fun(); 1055 ... 1056} 1057``` 1058 1059// b.h content 1060```cpp 1061int Fun(); 1062``` 1063 1064// b.cpp content 1065```cpp 1066int Fun() 1067{ 1068 // Do something 1069} 1070``` 1071In some scenarios, if internal functions need to be referenced with no intrusion to the code, the extern declaration mode can be used. 1072Example: 1073When performing unit testing on an internal function, you can use the extern declaration to reference the tested function. 1074When a function needs to be stubbed or patched, the function can be declared using extern. 1075 1076### <a name="r5-2-4"></a>Rule 5.2.4 Do not include header files in extern "C". 1077If a header file is included in extern "C", extern "C" may be nested. Some compilers restrict the nesting of extern "C". If there are too many nested layers, compilation errors may occur. 1078 1079When C and C++ programmings are used together and if extern "C" includes a header file, the original intent behind the header file may be hindered. For example, when the link specifications are modified incorrectly. 1080 1081For example, assume that there are two header files a.h and b.h. 1082 1083// a.h content 1084```cpp 1085... 1086#ifdef __cplusplus 1087void Foo(int); 1088#define A(value) Foo(value) 1089#else 1090void A(int) 1091#endif 1092``` 1093// b.h content 1094```cpp 1095... 1096#ifdef __cplusplus 1097extern "C" { 1098#endif 1099 1100#include "a.h" 1101void B(); 1102 1103#ifdef __cplusplus 1104} 1105#endif 1106``` 1107 1108Using the C++ preprocessor to expand b.h, the following information is displayed: 1109```cpp 1110extern "C" { 1111 void Foo(int); 1112 void B(); 1113} 1114``` 1115 1116According to the author of a.h, the function Foo is a C++ free function following the "C++" link specification. 1117However, because `#include "a.h"` is placed inside `extern "C"` in b.h, the link specification of function Foo is changed incorrectly. 1118 1119Exceptions: 1120In the C++ compilation environment, if you want to reference the header file of pure C, the C header files should not contain `extern "C"`. The non-intrusive approach is to include the C header file in `extern "C"`. 1121 1122### <a name="a5-2-1"></a>Rec 5.2.1 Use `#include` instead of a forward declaration to include header files. 1123A forward declaration is for the declaration of classes, functions, and templates and is not meant for its definition. 1124 1125- Advantages: 1126 1. Forward declarations can save compilation time. Redundant `#include `statements force the compiler to expand more files and process more input. 1127 2. Forward declarations can save unnecessary recompilation time. The use of #include will force your code to be recompiled multiple times due to unrelated changes in header files. 1128- Disadvantages: 1129 1. Forward declarations hide dependency relationship. When a header file is modified, user code will skip the necessary recompilation process. 1130 2. A forward declaration may be broken by subsequent changes to the library. Forward declarations of functions and templates sometimes prevent header file developers from changing APIs. For example, widening a formal parameter type, adding a template parameter with a default value, and so on. 1131 3. Forward declaration of symbols from the namespace `std::` is seen as undefined behavior (as specified in the C++ 11 standard specification). 1132 4. Forward declaration of multiple symbols from a header file can be more verbose than simply including (#include) the header. 1133 5. Structuring code only for forward declaration (for example, using pointer members instead of object members) can make the code more complex and slower. 1134 6. It is difficult to determine whether a forward declaration or `#include` is needed. In some scenarios, replacing `#include` with a forward declaration may cause unexpected results. 1135 1136Therefore, we should avoid using forward declarations as much as possible. Instead, we use the #include statement to include a header file and ensure dependency. 1137 1138# <a name="c6"></a>6 Scopes 1139 1140## <a name="c6-1"></a> Namespaces 1141 1142### <a name="a6-1-1"></a>Rec 6.1.1 For code that does not need to be exported from the .cpp file, you are advised to use an unnamed namespace for encapsulation or use static to modify the variables, constants, or functions that need hiding. 1143In the C++ 2003 standard, using static to modify the external availability of functions and variables was marked as deprecated. Therefore, unnamed namespaces are the recommended method. 1144 1145Main Reasons: 11461. There are too many meanings for static in C++: static function member variable, static member function, static global variable, and static function local variable. Each of them has special processing. 11472. Static can only be used to define variables, constants, and functions that are not referenced outside the current .cpp file, while namespaces can also be used to encapsulate types. 11483. Use a namespace to control the scope instead of using both static and namespaces. 11494. Unnamed namespaces can be used to instantiate templates rather than functions modified by the static keyword. 1150 1151Do not use unnamed namespaces or static in header files. 1152 1153```cpp 1154// Foo.cpp 1155 1156namespace { 1157 const int MAX_COUNT = 20; 1158 void InternalFun(){}; 1159} 1160 1161void Foo::Fun() 1162{ 1163 int i = MAX_COUNT; 1164 1165 InternalFun(); 1166} 1167 1168``` 1169 1170### <a name="r6-1-1"></a>Rule 6.1.1 Do not use "using" to import namespace in a header file or before #include statements. 1171Note: Using "using" to import namespace will affect any subsequent code and may cause symbol conflicts. 1172Example: 1173 1174```cpp 1175// Header file a.h 1176namespace NamespaceA { 1177 int Fun(int); 1178} 1179``` 1180 1181```cpp 1182// Header file b.h 1183namespace NamespaceB { 1184 int Fun(int); 1185} 1186 1187using namespace NamespaceB; 1188 1189void G() 1190{ 1191 Fun(1); 1192} 1193``` 1194 1195```cpp 1196// Source code a.cpp 1197#include "a.h" 1198using namespace NamespaceA; 1199#include "b.h" 1200 1201void main() 1202{ 1203 G(); // "using namespace NamespaceA" before #include "b.h", will cause conflicts when calling NamespaceA::Fun and NamespaceB::Fun. 1204} 1205``` 1206 1207Using "using" to import a symbol or define an alias in a header file is allowed in customized namespaces of modules, but is prohibited in the global namespace. 1208```cpp 1209// foo.h 1210 1211#include <fancy/string> 1212using fancy::string; // Bad: It is prohibited to import symbols to the global namespace. 1213 1214namespace Foo { 1215 using fancy::string; // Good: Symbols can be imported in customized namespaces of modules. 1216 using MyVector = fancy::vector; // Good: In C++11, aliases can be defined in customized namespaces. 1217} 1218``` 1219 1220 1221## <a name="c6-2"></a> Global Functions and Static Member Functions 1222 1223### <a name="a6-2-1"></a>Rec 6.2.1 Use namespaces to manage global functions. If global functions are closely tied to a class, you can use static member functions. 1224Note: Placing non-member functions in a namespace avoids polluting the global scope. Do not use "class + static member function" to simply manage global functions. If a global function is closely tied to a class, it can be used as a static member function of the class. 1225 1226If you need to define some global functions for a .cpp file, use unnamed namespaces for management. 1227```cpp 1228namespace MyNamespace { 1229 int Add(int a, int b); 1230} 1231 1232class File { 1233public: 1234 static File CreateTempFile(const std::string& fileName); 1235}; 1236``` 1237 1238## <a name="c6-3"></a> Global Constants and Static Member Constants 1239 1240### <a name="a6-3-1"></a>Rec 6.3.1 Use namespaces to manage global constants. If global constants are closely tied to a class, you can use static member constants. 1241Note: Placing global constants in a namespace avoids polluting the global scope. Do not use "class + static member constant" to simply manage global constants. If a global constant is closely tied to a class, it can be used as a static member constant of the class. 1242 1243If you need to define some global constants only for a .cpp file, use unnamed namespaces for management. 1244```cpp 1245namespace MyNamespace { 1246 const int MAX_SIZE = 100; 1247} 1248 1249class File { 1250public: 1251 static const std::string SEPARATOR; 1252}; 1253``` 1254 1255## <a name="c6-4"></a> Global Variables 1256 1257### <a name="a6-4-1"></a>Rec 6.4.1 Do not use global variables. Use the singleton pattern instead. 1258Note: Global variables can be modified and read, which results in data coupling between production code and the global variables. 1259```cpp 1260int g_counter = 0; 1261 1262// a.cpp 1263g_counter++; 1264 1265// b.cpp 1266g_counter++; 1267 1268// c.cpp 1269cout << g_counter << endl; 1270``` 1271 1272Singleton 1273```cpp 1274class Counter { 1275public: 1276 static Counter& GetInstance() 1277 { 1278 static Counter counter; 1279 return counter; 1280 } // Simple example of a singleton implementation 1281 1282 void Increase() 1283 { 1284 value_++; 1285 } 1286 1287 void Print() const 1288 { 1289 std::cout << value_ << std::endl; 1290 } 1291 1292private: 1293 Counter() : value_(0) {} 1294 1295private: 1296 int value_; 1297}; 1298 1299// a.cpp 1300Counter::GetInstance().Increase(); 1301 1302// b.cpp 1303Counter::GetInstance().Increase(); 1304 1305// c.cpp 1306Counter::GetInstance().Print(); 1307``` 1308 1309After the singleton is implemented, there is a unique global instance, which can functions as a global variable. However, the singleton provides better encapsulation. 1310 1311Exception: In some cases, the scope of a global variable is contained inside a module. Multiple instances of the same global variable may exist, and each module holds one copy. In this case, a singleton cannot be used as it is limited to one instance. 1312 1313# <a name="c7"></a>7 Classes 1314 1315## <a name="c7-1"></a> Constructors, Copy/Move Constructors, Copy/Move Assignment Operators, and Destructors 1316Constructors, copy/move constructors, copy/move assignment operators, and destructors provide lifetime management methods for objects. 1317- Constructor: `X()` 1318- Copy constructor: `X(const X&)` 1319- Copy assignment operator: `operator=(const X&)` 1320- Move constructor: `X (X&&)` *Provided in versions later than C++ 11*. 1321- Move assignment operator: `operator=(X&&)` *Provided in versions later than C++ 11*. 1322- Destructor: `~X()` 1323 1324### <a name="r7-1-1"></a>Rule 7.1.1 The member variables of a class must be initialized explicitly. 1325Note: If a class has members but no constructor and a default constructor is defined, the compiler will automatically generate a constructor, but it will not initialize member variables. The content of each object is uncertain. 1326 1327Exceptions: 1328- If the member variables in a class have a default constructor, explicit initialization is not required. 1329 1330Example: The following code has no constructor, and private data members cannot be initialized: 1331```cpp 1332class Message { 1333public: 1334 void ProcessOutMsg() 1335 { 1336 //... 1337 } 1338 1339private: 1340 unsigned int msgID_; 1341 unsigned int msgLength_; 1342 unsigned char* msgBuffer_; 1343 std::string someIdentifier_; 1344}; 1345 1346Message message; // The message member is not initialized. 1347message.ProcessOutMsg(); // Potential risks exist in subsequent use. 1348 1349// Therefore, it is necessary to define a default constructor as follows: 1350class Message { 1351public: 1352 Message() : msgID_(0), msgLength_(0), msgBuffer_(NULL) 1353 { 1354 } 1355 1356 void ProcessOutMsg() 1357 { 1358 // ... 1359 } 1360 1361private: 1362 unsigned int msgID_; 1363 unsigned int msgLength_; 1364 unsigned char* msgBuffer_; 1365 std::string someIdentifier; // The member variable has a default constructor. Therefore, explicit initialization is not required. 1366}; 1367``` 1368 1369### <a name="a7-1-1"></a>Rec 7.1.1 Initialization during declaration (C++ 11) and initialization using the constructor initialization list are preferred for member variables. 1370Note: Initialization during declaration (C++11) is preferred because initialized values of member variables can be easily understood. If initialized values of certain member variables are relevant to constructors, or C++ 11 is not supported, the constructor initialization list is used preferentially to initialize these member variables. Compared with the assignment statements in constructors, code of the constructor initialization list is simpler and has higher performance, and can be used to initialize constant and reference members. 1371 1372```cpp 1373class Message { 1374public: 1375 Message() : msgLength(0) { // Good: The constructor initialization list is preferred. 1376 { 1377 msgBuffer = NULL; // Bad: Values cannot be assigned in constructors. 1378 } 1379 1380private: 1381 unsigned int msgID{0}; // Good: Used in C++11. 1382 unsigned int msgLength_; 1383 unsigned char* msgBuffer_; 1384}; 1385``` 1386 1387### <a name="r7-1-2"></a>Rule 7.1.2 Declare single-parameter constructors as explicit to prevent implicit conversion. 1388Note: If a single-parameter constructor is not declared as explicit, it will become an implicit conversion function. 1389Example: 1390 1391```cpp 1392class Foo { 1393public: 1394 explicit Foo(const string& name): name_(name) 1395 { 1396 } 1397private: 1398 string name_; 1399}; 1400 1401 1402void ProcessFoo(const Foo& foo){} 1403 1404int main(void) 1405{ 1406 std::string test = "test"; 1407 ProcessFoo(test); // Compiling failed. 1408 return 0; 1409} 1410``` 1411 1412The preceding code fails to be compiled because the parameter required by `ProcessFoo` is of the Foo type, which mismatch with the input string type. 1413 1414If the explicit keyword of the Foo constructor is removed, implicit conversion is triggered and a temporary Foo object is generated when `ProcessFoo` is called with the string parameter. Usually, this implicit conversion is confusing and bugs are apt to be hidden, due to unexpected type conversion. Therefore, single-parameter constructors require explicit declaration. 1415 1416### <a name="r7-1-3"></a>Rule 7.1.3 If copy/move constructors and copy/move assignment operators are not needed, clearly prohibit them. 1417Note: If users do not define it, the compiler will generate copy constructors and copy assignment operators, move constructors and move assignment operators (move semantic functions will be available in versions later than C++ 11). 1418If we do not use copy constructors or copy assignment operators, explicitly delete them. 1419 14201. Set copy constructors or copy assignment operators to private and do not implement them. 1421```cpp 1422class Foo { 1423private: 1424 Foo(const Foo&); 1425 Foo& operator=(const Foo&); 1426}; 1427``` 14282. Use delete provided by C++ 11. For details, see Rule 10.1.3 in chapter 10 Modern C++ Features. 1429 1430 14313. You are advised to inherit **NoCopyable** and **NoMovable**. Do not use macros such as **DISALLOW_COPY_AND_MOVE**, **DISALLOW_COPY**, and **DISALLOW_MOVE**. 1432```cpp 1433class Foo : public NoCopyable, public NoMovable { 1434}; 1435``` 1436Implementation of NoCopyable and NoMovable: 1437```cpp 1438class NoCopyable { 1439public: 1440 NoCopyable() = default; 1441 NoCopyable(const NoCopyable&) = delete; 1442 NoCopyable& operator = (NoCopyable&) = delete; 1443}; 1444 1445class NoMovable { 1446public: 1447 NoMovable() = default; 1448 NoMovable(NoMovable&&) noexcept = delete; 1449 NoMovable& operator = (NoMovable&&) noexcept = delete; 1450}; 1451``` 1452 1453### <a name="r7-1-4"></a>Rule 7.1.4 Copy constructors and copy assignment operators should be implemented or forbidden together. 1454Both copy constructors and copy assignment operators provide copy semantics. They should be implemented or hidden together. 1455 1456```cpp 1457// The copy constructor and the copy assignment operator are implemented together. 1458class Foo { 1459public: 1460 ... 1461 Foo(const Foo&); 1462 Foo& operator=(const Foo&); 1463 ... 1464}; 1465 1466// The copy constructor and the copy assignment operator are both set to default, as supported by C++ 11. 1467class Foo { 1468public: 1469 Foo(const Foo&) = default; 1470 Foo& operator=(const Foo&) = default; 1471}; 1472 1473// The copy constructor and the copy assignment operator are hidden together. You should use the delete keyword if C++11 features are available. 1474class Foo { 1475private: 1476 Foo(const Foo&); 1477 Foo& operator=(const Foo&); 1478}; 1479``` 1480 1481### <a name="r7-1-5"></a>Rule 7.1.5 Move constructors and move assignment operators should be implemented or hidden together. 1482The move operation is added in C++ 11. If a class is required to support the move operation, move constructors and move assignment operators need to be implemented. 1483 1484Both move constructors and move assignment operators provide move semantics. They should be implemented or hidden together. 1485```cpp 1486// The copy constructor and the copy assignment operator are implemented together. 1487class Foo { 1488public: 1489 ... 1490 Foo(Foo&&); 1491 Foo& operator=(Foo&&); 1492 ... 1493}; 1494 1495// The copy constructor and the copy assignment operator are both set to default, as supported by C++ 11. 1496class Foo { 1497public: 1498 Foo(Foo&&) = default; 1499 Foo& operator=(Foo&&) = default; 1500}; 1501 1502// The copy constructor and the copy assignment operator are hidden together. You should use the delete keyword if C++11 features are available. 1503class Foo { 1504public: 1505 Foo(Foo&&) = delete; 1506 Foo& operator=(Foo&&) = delete; 1507}; 1508``` 1509 1510### <a name="r7-1-6"></a>Rule 7.1.6 It is prohibited to call virtual functions in constructors and destructors. 1511Note: Calling a virtual function of the current object in a constructor or destructor will cause behaviors of non-polymorphism. 1512In C++, a base class constructs only one complete object at a time. 1513 1514Example: Base indicates the base class, and Sub indicates the derived class. 1515```cpp 1516class Base { 1517public: 1518 Base(); 1519 virtual void Log() = 0; // Different derived classes call different log files. 1520}; 1521 1522Base::Base() // Base class constructor 1523{ 1524 Log(); // Call the virtual function log. 1525} 1526 1527class Sub : public Base { 1528public: 1529 virtual void Log(); 1530}; 1531``` 1532 1533When running the following statement: 1534`Sub sub;` 1535The constructor of the derived class is executed first. However, the constructor of the base class is called first. Because the constructor of the base class calls the virtual function log, the log is in the base class version. The derived class is constructed only after the base class is constructed. As a result, behaviors of non-polymorphism are caused. 1536This also applies to destructors. 1537 1538### <a name="r7-1-7"></a>Rule 7.1.7 The copy constructors, copy assignment operators, move constructors, and move assignment operators in polymorphic base classes must be non-public or delete functions. 1539Slicing occurs if the value of a derived class object is directly assigned to a base class object. In this case, only the base class part is copied or moved, which undermines polymorphism. 1540[Counterexample] 1541In the following code example, the copy constructor and copy assignment operator are not defined in the base class. The compiler automatically generates two special member functions. 1542If the value of a derived class object is assigned to the base class object, slicing occurs. The copy constructor and copy assignment operator can be declared as **deleted** so that the compiler can check such assignment behavior. 1543```cpp 1544class Base { 1545public: 1546 Base() = default; 1547 virtual ~Base() = default; 1548 ... 1549 virtual void Fun() { std::cout << "Base" << std::endl;} 1550}; 1551 1552class Derived : public Base { 1553 ... 1554 void Fun() override { std::cout << "Derived" << std::endl; } 1555}; 1556 1557void Foo(const Base &base) 1558{ 1559 Base other = base; // Bad: Slicing occurs 1560 other.Fun(); // The Fun() function of the base class is called. 1561} 1562``` 1563```cpp 1564Derived d; 1565Foo(d); // A derived class object is passed. 1566``` 15671. Set copy constructors or copy assignment operators to **private** and do not implement them. 1568 1569## <a name="c7-2"></a> Inheritance 1570 1571### <a name="r7-2-1"></a>Rule 7.2.1 Declare destructors of a base class as virtual, and declare the class that is not to be inherited as final. 1572Note: Destructors of the derived class can be called during polymorphism invocation only when destructors of the base class are virtual. 1573 1574Example: There will be memory leak if destructors of the base class are not declared as virtual. 1575```cpp 1576class Base { 1577public: 1578 virtual std::string getVersion() = 0; 1579 1580 ~Base() 1581 { 1582 std::cout << "~Base" << std::endl; 1583 } 1584}; 1585``` 1586 1587```cpp 1588class Sub : public Base { 1589public: 1590 Sub() : numbers_(NULL) 1591 { 1592 } 1593 1594 ~Sub() 1595 { 1596 delete[] numbers_; 1597 std::cout << "~Sub" << std::endl; 1598 } 1599 1600 int Init() 1601 { 1602 const size_t numberCount = 100; 1603 numbers_ = new (std::nothrow) int[numberCount]; 1604 if (numbers_ == NULL) { 1605 return -1; 1606 } 1607 1608 ... 1609 } 1610 1611 std::string getVersion() 1612 { 1613 return std::string("hello!"); 1614 } 1615private: 1616 int* numbers_; 1617}; 1618``` 1619 1620```cpp 1621int main(int argc, char* args[]) 1622{ 1623 Base* b = new Sub(); 1624 1625 delete b; 1626 return 0; 1627} 1628``` 1629Because destructors of the base class are not declared as virtual, only destructors of the base class are called when an object is destroyed. Destructors of the derived class Sub are not called. As a result, a memory leak occurs. 1630Exceptions: 1631For classes such as **NoCopyable** and **NoMovable** that have no behavior and are used only as identifiers, you do not need to define them as final. 1632 1633### <a name="r7-2-2"></a>Rule 7.2.2 Do not use default parameter values for virtual functions. 1634Note: In C++, virtual functions are dynamically bound, but the default parameters of functions are statically bound during compilation. This means that the function you finally execute is a virtual function that is defined in the derived class but uses the default parameter value in the base class. To avoid confusion and other problems caused by inconsistent default parameter declarations during overriding of virtual functions, it is prohibited to declare default parameter values for all virtual functions. 1635Example: The default value of parameter "text" of the virtual function "Display" is determined at compilation time instead of runtime, which does not fit with polymorphism. 1636```cpp 1637class Base { 1638public: 1639 virtual void Display(const std::string& text = "Base!") 1640 { 1641 std::cout << text << std::endl; 1642 } 1643 1644 virtual ~Base(){} 1645}; 1646 1647class Sub : public Base { 1648public: 1649 virtual void Display(const std::string& text = "Sub!") 1650 { 1651 std::cout << text << std::endl; 1652 } 1653 1654 virtual ~Sub(){} 1655}; 1656 1657int main() 1658{ 1659 Base* base = new Sub(); 1660 Sub* sub = new Sub(); 1661 1662 ... 1663 1664 base->Display(); // The program output is as follows: Base! The expected output is as follows: Sub! 1665 sub->Display(); // The program output is as follows: Sub! 1666 1667 delete base; 1668 delete sub; 1669 return 0; 1670}; 1671``` 1672 1673### <a name="r7-2-3"></a>Rule 7.2.3 Do not redefine inherited non-virtual functions. 1674Note: Non-virtual functions cannot be dynamically bound (only virtual functions can be dynamically bound). You can obtain the correct result by operating on the pointer of the base class. 1675 1676Example: 1677```cpp 1678class Base { 1679public: 1680 void Fun(); 1681}; 1682 1683class Sub : public Base { 1684public: 1685 void Fun(); 1686}; 1687 1688Sub* sub = new Sub(); 1689Base* base = sub; 1690 1691sub->Fun(); // Call Fun of the derived class. 1692base->Fun(); // Call Fun of the base class. 1693//... 1694 1695``` 1696 1697## <a name="c7-3"></a> Multiple Inheritance 1698In the actual development process, multiple inheritance is seldom used because the following typical problems may occur: 16991. Data duplication and name ambiguity caused by "diamond" inheritance. C++ introduces virtual inheritance to solve these problems. 17002. In addition to "diamond" inheritance, names of multiple base classes may also conflict with each other, resulting in name ambiguity. 17013. If a derived class needs to be extended or needs to override methods of multiple base classes, the responsibilities of the derived classes are unclear and semantics are muddled. 17024. Compared with delegation, inheritance is seen as white box reuse, that is, a derived class can access the protected members of the base class, which leads to more coupling. Multiple inheritance, due to the coupling of multiple base classes, leads to even more coupling. 1703 1704Multiple inheritance has the following advantages: 1705Multiple inheritance provides a simpler method for assembling and reusing multiple interfaces or classes. 1706 1707Therefore, multiple inheritance can be used only in the following cases: 1708 1709### <a name="a7-3-1"></a>Rec 7.3.1 Use multi-inheritance to implement interface separation and multi-role combination. 1710If a class requires multiple interfaces, combine multiple separated interfaces by using multiple inheritance. This is similar to the Traits mixin of the Scala language. 1711 1712```cpp 1713class Role1 {}; 1714class Role2 {}; 1715class Role3 {}; 1716 1717class Object1 : public Role1, public Role2 { 1718 // ... 1719}; 1720 1721class Object2 : public Role2, public Role3 { 1722 // ... 1723}; 1724 1725``` 1726 1727The C++ standard library has a similar implementation example: 1728```cpp 1729class basic_istream {}; 1730class basic_ostream {}; 1731 1732class basic_iostream : public basic_istream, public basic_ostream { 1733 1734}; 1735``` 1736 1737## <a name="c7-4"></a> Overloading 1738 1739Overload operators should be used when there are sufficient reasons, and they do not change the original perception of the operators. For example, do not use the plus sign (+) to perform subtraction. 1740Operator overloading can make code more intuitive but has some disadvantages: 1741- It is often mistaken that the operation is as fast as a built-in operator, which has no performance degradation. 1742- There is no naming to aid debugging. It is more convenient to search by function name than by operator. 1743- Overloading operators can cause confusion if behavior definitions are not intuitive (for example, if the "+" operator is used for subtraction). 1744- The implicit conversion caused by the overloading of assignment operators may lead to entrenched bugs. Functions such as Equals () and CopyFrom () can be defined to replace the = and == operators. 1745 1746 1747 1748# <a name="c8"></a> 8 Functions 1749## <a name="c8-1"></a>Function Design 1750### <a name="r8-1-1"></a>Rule 8.1.1 Avoid long functions and ensure that each function contains no more than 50 lines (non-null and non-comment). 1751A function should be displayed on one screen (no longer than 50 lines). It should do only one thing, and do it well. 1752 1753Long functions often mean that the functions are too complex to implement in more than one function, or overly detailed but not further abstracted. 1754 1755Exception: Some algorithms may be longer than 50 lines due to algorithm convergence and functional comprehensiveness. 1756 1757Even if a long function works very well now, once someone modifies it, new problems may occur. It might even cause bugs that are difficult to find. 1758It is recommended that you split a long function into several functions that are simpler and easier to manage, facilitating comprehension and modification. 1759 1760## <a name="c8-2"></a>Inline Functions 1761 1762### <a name="a8-2-1"></a>Rec 8.2.1 An inline function cannot exceed 10 lines. 1763**Note**: An inline function has the same characteristics of a normal function. The difference between an inline function and a normal function lies in the processing of function calls. When a general function is called, the program execution right is transferred to the called function, and then returned to the function that calls it. When an inline function is called, the invocation expression is replaced with an inline function body. 1764 1765Inline functions are only suitable for small functions with only 1-10 lines. For a large function that contains many statements, the function call and return overheads are relatively trivial and do not need the help of an inline function. Most compilers may abandon the inline mode and use the common method to call the function. 1766 1767If an inline function contains complex control structures, such as loop, branch (switch), and try-catch statements, the compiler may regard the function as a common function. 1768**Virtual functions and recursive functions cannot be used as inline functions**. 1769 1770## <a name="c8-3"></a> Function Parameters 1771 1772### <a name="a8-3-1"></a>Rec 8.3.1 Use a reference instead of a pointer for function parameters. 1773 1774**Note**: A reference is more secure than a pointer because it is not empty and does not point to other targets. Using a reference stops the need to check for illegal null pointers. 1775 1776If a product is being developed for an older platform, the processing used by the old platform is preferred. 1777Use const to avoid parameter modification, so that readers can clearly know that a parameter is not going to be modified. This greatly enhances code readability. 1778 1779Exception: When the input parameter is an array with an unknown compile-time length, you can use a pointer instead of a reference. 1780 1781### <a name="a8-3-2"></a>Rec 8.3.2 Use strongly typed parameters. Do not use void*. 1782While different languages have their own views on strong typing and weak typing, it is generally believed that C and C++ are strongly typed languages. Since we use such a strongly typed language, we should keep to this style. 1783An advantage of this is the compiler can find type mismatch problems at the compilation stage. 1784 1785Using strong typing helps the compiler find more errors for us. Pay attention to the usage of the FooListAddNode function in the following code: 1786```cpp 1787struct FooNode { 1788 struct List link; 1789 int foo; 1790}; 1791 1792struct BarNode { 1793 struct List link; 1794 int bar; 1795} 1796 1797void FooListAddNode(void *node) // Bad: Here, the void * type is used to transfer parameters. 1798{ 1799 FooNode *foo = (FooNode *)node; 1800 ListAppend(&g_FooList, &foo->link); 1801} 1802 1803void MakeTheList() 1804{ 1805 FooNode *foo = NULL; 1806 BarNode *bar = NULL; 1807 ... 1808 1809 FooListAddNode(bar); // Wrong: In this example, the foo parameter was supposed to be transferred, but the bar parameter is accidentally transferred instead. However, no error is reported. 1810} 1811``` 1812 18131. You can use a template function to change the parameter type. 18142. A base class pointer can be used to implement this according to polymorphism. 1815 1816### <a name="a8-3-3"></a>Rec 8.3.3 A function can have a maximum of five parameters. 1817If a function has too many parameters, it is apt to be affected by external changes, and therefore maintenance is affected. Too many function parameters will also increase the testing workload. 1818 1819If a function has more than five parameters, you can: 1820- Split the function. 1821- Combine related parameters into a struct. 1822 1823# <a name="c9"></a> 9 Other C++ Features 1824 1825## <a name="c9-1"></a> Constants and Initialization 1826 1827Unchanged values are easier to understand, trace, and analyze. Therefore, use constants instead of variables as much as possible. When defining values, use const as a default. 1828 1829### <a name="r9-1-1"></a>Rule 9.1.1 Do not use macros to replace constants. 1830 1831**Note**: Macros are a simple text replacement that is completed in the preprocessing phase. When an error is reported, the corresponding value is reported. During tracing and debugging, the value is also displayed instead of the macro name. A macro does not support type checking and is insecure. A macro has no scope. 1832 1833```cpp 1834#define MAX_MSISDN_LEN 20 // Bad 1835 1836// Use const in C++. 1837const int MAX_MSISDN_LEN = 20; // Good 1838 1839// In versions later than C++ 11, constexpr can be used. 1840constexpr int MAX_MSISDN_LEN = 20; 1841``` 1842 1843### <a name="a9-1-1"></a>Rec 9.1.1 A group of related integer constants must be defined as an enumeration. 1844 1845**Note**: Enumerations are more secure than `#define` or `const int`. The compiler checks whether a parameter value is within the enumerated value range to avoid errors. 1846 1847```cpp 1848// Good example: 1849enum Week { 1850 SUNDAY, 1851 MONDAY, 1852 TUESDAY, 1853 WEDNESDAY, 1854 THURSDAY, 1855 FRIDAY, 1856 SATURDAY 1857}; 1858 1859enum Color { 1860 RED, 1861 BLACK, 1862 BLUE 1863}; 1864 1865void ColorizeCalendar(Week today, Color color); 1866 1867ColorizeCalendar(BLUE, SUNDAY); // Compilation error. The parameter type is incorrect. 1868 1869// Bad example: 1870const int SUNDAY = 0; 1871const int MONDAY = 1; 1872 1873const int BLACK = 0; 1874const int BLUE = 1; 1875 1876bool ColorizeCalendar(int today, int color); 1877ColorizeCalendar(BLUE, SUNDAY); // No error is reported. 1878``` 1879 1880When an enumeration value needs to correspond to a specific value, explicit value assignment is required during declaration. Otherwise, do not assign explicit values. This will prevent repeated assignment and reduce the maintenance workload (when adding and deleting members). 1881 1882```cpp 1883// Good example: Device ID defined in the S protocol. It is used to identify a device type. 1884enum DeviceType { 1885 DEV_UNKNOWN = -1, 1886 DEV_DSMP = 0, 1887 DEV_ISMG = 1, 1888 DEV_WAPPORTAL = 2 1889}; 1890``` 1891 1892Do not assign explicit values when enumeration is used internally, and only for classification. 1893 1894```cpp 1895// Good example: Enumeration definition is used to identify session status in a program. 1896enum SessionState { 1897 INIT, 1898 CLOSED, 1899 WAITING_FOR_RESPONSE 1900}; 1901``` 1902 1903Try to avoid repeating enumeration values. If it is required, use the already defined enumeration values instead. 1904 1905```cpp 1906enum RTCPType { 1907 RTCP_SR = 200, 1908 RTCP_MIN_TYPE = RTCP_SR, 1909 RTCP_RR = 201, 1910 RTCP_SDES = 202, 1911 RTCP_BYE = 203, 1912 RTCP_APP = 204, 1913 RTCP_RTPFB = 205, 1914 RTCP_PSFB = 206, 1915 RTCP_XR = 207, 1916 RTCP_RSI = 208, 1917 RTCP_PUBPORTS = 209, 1918 RTCP_MAX_TYPE = RTCP_PUBPORTS 1919}; 1920``` 1921 1922### <a name="r9-1-2"></a>Rule 9.1.2 Magic numbers cannot be used. 1923So-called magic numbers are numbers that are unintelligible and difficult to understand. 1924 1925Some numbers can be understood based on context. 1926For example, the number 12 varies in different contexts. 1927type = 12; is not intelligible (and a magic number), but `month = year * 12`; can be understood, so we wouldn't really class this as a magic number. 1928The number 0 is often seen as a magic number. For example, `status = 0`; cannot truly express any status information. 1929 1930Solution: 1931Comments can be added for numbers that are used locally. 1932For the numbers that are used multiple times, you must define them as constants and give them descriptive names. 1933 1934The following cases are forbidden: 1935No symbol is used to explain the meaning of a number, for example, ` const int ZERO = 0`. 1936The symbol name limits the value. For example, for example, `const int XX_TIMER_INTERVAL_300MS = 300`. Use `XX_TIMER_INTERVAL_MS` instead. 1937 1938### <a name="r9-1-3"></a>Rule 9.1.3 Ensure that a constant has only one responsibility. 1939 1940**Note**: A constant is used for only one specific function, that is, a constant cannot be used for multiple purposes. 1941 1942```cpp 1943// Good example: For protocol A and protocol B, the length of the MSISDN is 20. 1944const unsigned int A_MAX_MSISDN_LEN = 20; 1945const unsigned int B_MAX_MSISDN_LEN = 20; 1946 1947// Using different namespaces: 1948namespace Namespace1 { 1949 const unsigned int MAX_MSISDN_LEN = 20; 1950} 1951 1952namespace Namespace2 { 1953 const unsigned int MAX_MSISDN_LEN = 20; 1954} 1955``` 1956 1957### <a name="r9-1-4"></a>Rule 9.1.4 Do not use memcpy_s or memset_s to initialize non-POD objects. 1958 1959**Note**: `POD` is short for `Plain Old Data`, which is a concept introduced in the C++ 98 standard (ISO/IEC 14882, first edition, 1998-09-01). The `POD` types include the original types and aggregate types such as `int`, `char`, `float`, `double`, `enumeration`, `void`, and pointer. Encapsulation and object-oriented features cannot be used (for example, user-defined constructors, assignment operators, destructors, base classes, and virtual functions). 1960 1961For non-POD classes, such as class objects of non-aggregate types, virtual functions may exist. Memory layout is uncertain, and is related to the compiler. Misuse of memory copies may cause serious problems. 1962 1963Even if a class of the aggregate type is directly copied and compared, and any functions hiding information or protecting data are destroyed, the `memcpy_s` and `memset_s` operations are not recommended. 1964 1965For details about the POD type, see the appendix. 1966 1967### <a name="a9-1-2"></a>Rec 9.1.2 Declare and initialize variables only when they are used. 1968 1969**Note**: It is a common low-level programming error that a variable is not assigned an initial value before being used. Declaring and initializing a variable just before using it will prevent this. 1970 1971If all variables are declared at the beginning of a function before they are used, their scope covers the entire function, which may lead to the following problems: 1972* The program may become difficult to understand and maintain. The definition and use of variables are separated. 1973* These variables are difficult to initialize properly. At the beginning of a function, there is often insufficient information for variable initialization, and a default null value (such as 0) is often assigned as the initial value. If a variable is used before it is assigned a valid value, it will also cause errors. 1974 1975Following the minimization principle of variable scopes and the principle of proximity declaration will make it easier to read code and understand variable types and initial values. In particular, use initialization to replace declaration and then assign values. 1976 1977```cpp 1978// Bad example: Declaration is separated from initialization. 1979string name; // The variable is not initialized in the declaration, and a default constructor is called. 1980name = "zhangsan"; // An assignment operator is called again. Declaration is separate from definition, which is difficult to understand. 1981 1982// Good example: Declaration and initialization are together, and easy to understand. 1983string name("zhangsan"); // Invoke a constructor. 1984``` 1985 1986 1987## <a name="c9-2"></a> Expressions 1988### <a name="r9-2-1"></a>Rule 9.2.1 A variable cannot be referenced again if it is contained in an increment or decrement operation in an expression. 1989In an expression where the increment or decrement operations are performed on a variable, the variable cannot be referenced again. The result of a second referencing is not explicitly defined in C++ standards. The results in different compilers or different versions of a compiler may be different. 1990Therefore, it is recommended that an undefined operation sequence not be assumed. 1991 1992Note that the problem of operation sequence cannot be solved by using parentheses because this is not a priority problem. 1993 1994Example: 1995```cpp 1996x = b[i] + i++; // Bad: Whether the position of b[i] is before or after the i++ is unclear. 1997``` 1998The increment or decrement operation should be placed in a single line: 1999```cpp 2000x = b[i] + i; 2001i++; // Good: i++ is placed in a single line. 2002``` 2003 2004Function parameter 2005```cpp 2006Func(i++, i); // Bad: Whether the increment operation happens for the second parameter is unclear 2007``` 2008 2009Good example: 2010```cpp 2011i++; // Good: i++ is placed in a single line. 2012x = Func(i, i); 2013``` 2014 2015### <a name="r9-2-2"></a>Rule 9.2.2 A switch statement must have a default branch. 2016In most cases, a switch statement requires a default branch to ensure that there is a default action when the case tag is missing for a processed value. 2017 2018Exceptions: 2019If the switch condition variables are enumerated and the case branch covers all values, the default branch is redundant. 2020Because modern compilers can check which case branches are missing in the switch statement and provide an advanced warning. 2021 2022```cpp 2023enum Color { 2024 RED = 0, 2025 BLUE 2026}; 2027 2028// The switch condition variables are enumerated. Therefore, you do not need to add a default branch. 2029switch (color) { 2030 case RED: 2031 DoRedThing(); 2032 break; 2033 case BLUE: 2034 DoBlueThing(); 2035 ... 2036 break; 2037} 2038``` 2039 2040### <a name="a9-2-1"></a>Rec 9.2.1 When comparing expressions, follow the principle that the left side tends to change and the right side tends to remain unchanged. 2041When a variable is compared with a constant, placing the constant on the left, for example, if (MAX == v), does not comply with standard reading habits and is more difficult to understand. 2042The constant should be placed on the right. The expression is written as follows: 2043```cpp 2044if (value == MAX) { 2045 2046} 2047 2048if (value < MAX) { 2049 2050} 2051``` 2052There are special cases: for example, if the expression `if (MIN < value && value < MAX)` is used to describe a range, the first half, as a constant, should be placed on the left. 2053 2054You do not need to worry about writing '==' as '=' because a compilation alarm will be generated for `if (value = MAX)` and an error will be reported by other static check tools. Use these tools to solve such writing errors and ensure that that code is readable. 2055 2056### <a name="a9-2-2"></a>Rec 9.2.2 Use parentheses to specify the operator precedence. 2057Use parentheses to specify the operator precedence. This will prevent program errors due to the inconsistency between default priority and the intended design. At the same time, it makes the code clearer and more readable. However, too many parentheses muddy the code, reducing readability. The following is a recommendation on their correct usage. 2058 2059- For binary and ternary operators, if multiple operators are involved, parentheses should be used. 2060```cpp 2061x = a + b + c; /* The operator does not change, and thus parentheses are not required. */ 2062x = Foo(a + b, c); /* The operator does not change, and thus parentheses are not required. */ 2063x = 1 << (2 + 3); /* More than one operator is used and thus parentheses are required. */ 2064x = a + (b / 5); /* More than one operator is used and thus parentheses are required. */ 2065x = (a == b) ? a : (a – b); /* More than one operator is used and thus parentheses are required. */ 2066``` 2067 2068 2069## <a name="c9-3"></a> Type Casting 2070 2071Do not use type branches to customize behaviors. Type branch customization behavior is prone to errors and is an obvious sign of attempting to compile C code using C++. This is very inflexible technology. If you forget to modify all branches when adding a new type to a compiler, you will not be notified. Use templates and virtual functions to let the type define itself rather than letting the calling side determine behavior. 2072 2073It is recommended that type casting be avoided. We should consider the data type in the code design instead of overusing type casting to solve type conflicts. When designing a basic type, consider the following: 2074- Whether it is unsigned or signed. 2075- Is it suitable for float or double? 2076- Should you use int8, int16, int32, or int64 bit lengths? 2077 2078However, we cannot prohibit the use of type casting because the C++ language is a machine-oriented programming language, involving pointer addresses, and we interact with various third-party or underlying APIs. Their type design may not be reasonable and type casting tends to occur in the adaptation process. 2079 2080Exception: When calling a function, if we do not want to process the result of the function, first consider whether this is your best choice. If you do not want to process the return value of the function, cast it to void. 2081 2082### <a name="r9-3-1"></a>Rule 9.3.1 If type casting is required, use the type casting provided by the C++ instead of the C style. 2083 2084**Note**: 2085 2086The type casting provided by C++ is more targeted, easy to read, and more secure than the C style. C++ provides the following types of casting: 2087- Type casting: 20881. `dynamic_cast`: Used to inherit the downstream transformation of the system. `dynamic_cast` has the type check function. Design the base class and derived class to avoid using dynamic_cast for casting. 20892. `static_cast`: Similar to the C style casting, which can be used to convert a value, or to convert the pointer or reference of a derived class into a base class pointer or reference. This casting is often used to eliminate type ambiguity brought on by multiple inheritance, which is relatively safe. If it is a pure arithmetic conversion, use the braces as stated in the following text. 20903. `reinterpret_cast`: Used to convert irrelevant types. `reinterpret_cast` forces the compiler to reinterpret the memory of a certain type of objects into another type, which is an unsafe conversion. It is recommended that `reinterpret_cast` be used as little as possible. 20914. `const_cast`: Used to remove the `const` attribute of an object so that the object can be modified. You are advised to use `const_cast` as little as possible. 2092 2093- Arithmetic conversion: (Supported by C++ 11 and later versions) 2094 If the type information is not lost, for example, the casting from float to double, or from int32 to int64, the braces syntax is recommended. 2095```cpp 2096 double d{ someFloat }; 2097 int64_t i{ someInt32 }; 2098``` 2099 2100### <a name="a9-3-1"></a>Rec 9.3.1 Avoid using `dynamic_cast`. 21011. `dynamic_cast` depends on the RTTI of C++ so that the programmer can identify the type of the object in C++ at run time. 21022. `dynamic_cast` indicates that a problem has occurred in the design of the base class and derived class.The derived class destroys the contract of the base class and it is necessary to use `dynamic_cast` to convert the class to a subclass for special processing. In this case, it is more desirable to improve the design of the class, instead of using `dynamic_cast` to solve the problem. 2103 2104### <a name="a9-3-2"></a>Rec 9.3.2 Avoid using `reinterpret_cast`. 2105 2106**Note**: `reinterpret_cast` is used to convert irrelevant types. Trying to use `reinterpret_cast` to force a type to another type destroys the security and reliability of the type and is an insecure casting method. Avoid casting between completely different types. 2107 2108### <a name="a9-3-3"></a>Rec 9.3.3 Avoid using `const_cast`. 2109 2110**Note**: The `const_cast` command is used to remove the `const` and `volatile` properties of an object. 2111 2112The action of using a pointer or reference after the const_cast conversion to modify the const property of an object is undefined. 2113 2114```cpp 2115// Bad example: 2116const int i = 1024; 2117int* p = const_cast<int*>(&i); 2118*p = 2048; // The action is undefined. 2119``` 2120 2121```cpp 2122// Bad example: 2123class Foo { 2124public: 2125 Foo() : i(3) {} 2126 2127 void Fun(int v) 2128 { 2129 i = v; 2130 } 2131 2132private: 2133 int i; 2134}; 2135 2136int main(void) 2137{ 2138 const Foo f; 2139 Foo* p = const_cast<Foo*>(&f); 2140 p->Fun(8); // The action is undefined. 2141} 2142 2143``` 2144 2145 2146## <a name="c9-4"></a>Resource Allocation and Release 2147 2148### <a name="r9-4-1"></a>Rule 9.4.1 When a single object is released, delete is used. When an array object is released, delete [] is used. 2149Note: To delete a single object, use delete; to delete an array object, use delete []. The reasons are as follows: 2150 2151- new: Apply for memory from the system and call the corresponding constructor to initialize an object. 2152- new[n]: Apply for memory for n objects and call the constructor n times for each object to initialize them. 2153- delete: Call the corresponding destructor first and release the memory of an object. 2154- delete[]: Call the corresponding destructor for each object and release their memory. 2155 2156If the usage of new and delete does not match this format, the results are unknown. For a non-class type, new and delete will not call the constructor or destructor. 2157 2158Bad example: 2159```cpp 2160const int MAX_ARRAY_SIZE = 100; 2161int* numberArray = new int[MAX_ARRAY_SIZE]; 2162... 2163delete numberArray; 2164numberArray = NULL; 2165``` 2166 2167Good example: 2168```cpp 2169const int MAX_ARRAY_SIZE = 100; 2170int* numberArray = new int[MAX_ARRAY_SIZE]; 2171... 2172delete[] numberArray; 2173numberArray = NULL; 2174``` 2175 2176### <a name="a9-4-1"></a>Rec 9.4.1 Use the RAII feature to trace dynamic allocation. 2177 2178Note: RAII is an acronym for Resource Acquisition Is Initialization. It is a simple technology that controls program resources (such as memory, file handle, network connections, and mutexes) by using the object lifecycle. 2179 2180The common practice is as follows: When the object is constructed, the resource is obtained, and the access to the resource is controlled so that the resource is always valid in the life cycle of the object. Finally, the resource is released when the object is destructed. This approach has two advantages: 2181- We do not need to explicitly release resources. 2182- The resources required by the object are always valid throughout the lifecycle of the object. This way, you do not need to check the validity of the resources, which simplifies logic and improves efficiency. 2183 2184 2185In the following example, RAII removes the need for explicit release of mutex resources. 2186 2187```cpp 2188class LockGuard { 2189public: 2190 LockGuard(const LockType& lockType): lock_(lockType) 2191 { 2192 lock_.Acquire(); 2193 } 2194 2195 ~LockGuard() 2196 { 2197 lock_.Release(); 2198 } 2199 2200private: 2201 LockType lock_; 2202}; 2203 2204 2205bool Update() 2206{ 2207 LockGuard lockGuard(mutex); 2208 if (...) { 2209 return false; 2210 } else { 2211 // Data operations 2212 } 2213 2214 return true; 2215} 2216``` 2217 2218## <a name="c9-5"></a>Standard Template Library 2219 2220The standard template library (STL) varies between products. The following table lists some basic rules and suggestions for each team. 2221 2222### <a name="r9-5-1"></a>Rule 9.5.1 Do not save the pointer returned by c_str () of std::string. 2223 2224Note: The C++ standard does not specify that the string::c_str () pointer is permanently valid. Therefore, the STL implementation used can return a temporary storage area and release it quickly when calling string::c_str (). Therefore, to ensure the portability of the program, do not save the result of string::c_str (). Instead, call it directly. 2225 2226Example: 2227 2228```cpp 2229void Fun1() 2230{ 2231 std::string name = "demo"; 2232 const char* text = name.c_str(); // After the expression ends, the life cycle of name is still in use and the pointer is valid. 2233 2234 // If a non-const member function (such as operator[] and begin()) of the string type is invoked and the string is modified, 2235 // The text may become unavailable or may not be the original string. 2236 name = "test"; 2237 name[1] = '2'; 2238 2239 // When the text pointer is used next time, the string is no longer "demo". 2240} 2241 2242void Fun2() 2243{ 2244 std::string name = "demo"; 2245 std::string test = "test"; 2246 const char* text = (name + test).c_str(); // After the expression ends, the temporary object generated by the + operator may be destroyed, and the pointer may be invalid. 2247 2248 // When the text pointer is used next time, it no longer points to the valid memory space. 2249} 2250``` 2251Exception: In rare cases where high performance coding is required , you can temporarily save the pointer returned by string::c_str() to match the existing functions which support only the input parameters of the const char* type. However, you should ensure that the lifecycle of the string object is longer than that of the saved pointer, and that the string object is not modified within the lifecycle of the saved pointer. 2252 2253 2254### <a name="a9-5-1"></a>Rec 9.5.1 Use std::string instead of char*. 2255 2256Note: Using string instead of `char*` has the following advantages: 22571. There is no need to consider the null character '\0' at the end. 22582. You can directly use operators such as `+`, `=`, and `==`, and other character and string operation functions. 22593. There is no need to consider memory allocation operations.This helps avoid explicit usage of `new` and `delete` and the resulting errors. 2260 2261Note that in some STL implementations, string is based on the copy-on-write policy, which causes two problems. One is that the copy-on-write policy of some versions does not implement thread security, and the program breaks down in multi-threaded environments. Second, dangling pointers may be caused when a dynamic link library transfers the string based on the copy-on-write policy, due to the fact that reference count cannot be reduced when the library is unloaded. Therefore, it is important to select a reliable STL implementation to ensure the stability of the program. 2262 2263Exception: 2264When an API of a system or other third-party library is called, only `char*` can be used for defined interfaces. However, before calling the interfaces, you can use string. When calling the interfaces, you can use `string::c_str()` to obtain the character pointer. 2265When a character array is allocated as a buffer on the stack, you can directly define the character array without using string or containers such as `vector<char>`. 2266 2267### <a name="r9-5-2"></a>Rule 9.5.2 Do not use auto_ptr. 2268Note: The `std::auto_ptr` in the STL library has an implicit ownership transfer behavior. The code is as follows: 2269```cpp 2270auto_ptr<T> p1(new T); 2271auto_ptr<T> p2 = p1; 2272``` 2273After the second line of statements is executed, p1 does not point to the object allocated in line 1 and becomes `NULL`. Therefore, `auto_ptr` cannot be placed in any standard containers. 2274This ownership transfer behavior is not expected. In scenarios where ownership must be transferred, implicit transfer should not be used. This often requires the programmer to keep extra attention on code that uses `auto_ptr`, otherwise access to a null pointer will occur. 2275There are two common scenarios for using auto_ptr . One is to transfer it as a smart pointer to outside the function that generates the auto_ptr , and the other is to use auto_ptr as the RAII management class. Resources are automatically released when the lifecycle of auto_ptr expires. 2276In the first scenario, you can use std::shared_ptr instead. 2277In the second scenario, you can use std::unique_ptr in the C++ 11 standard. std::unique_ptr is a substitute for std::auto_ptr and supports explicit ownership transfer. 2278 2279Exception: 2280Before the C++ 11 standard is widely used, std::auto_ptr can be used in scenarios where ownership needs to be transferred. However, it is recommended that std::auto_ptr be encapsulated. The copy constructor and assignment operator of the encapsulation class should not be used in a standard container. 2281 2282 2283### <a name="a9-5-2"></a>Rec 9.5.2 Use the new standard header files. 2284 2285Note: 2286When using the standard header file of C++, use `<cstdlib>` instead of `<stdlib.h>`. 2287 2288## <a name="c9-6"></a> Usage of const 2289Add the keyword const before the declared variable or parameter (example: `const int foo`) to prevent the variable from being tampered with. Add the const qualifier to the function in the class (example: `class Foo {int Bar (char c) const;} ;`) to make sure the function does not modify the status of the class member variable. const variables, data members, functions, and parameters ensure that the type detection during compilation is accurate and errors are found as soon as possible. Therefore, we strongly recommend that const be used in any possible case. 2290Sometimes it is better to use constexpr from C++ 11 to define real constants. 2291 2292### <a name="r9-6-1"></a>Rule 9.6.1 For formal parameters of pointer and reference types, if the parameters do not need to be modified, use const. 2293Unchanging values are easier to understand, trace, and analyze. `const` is used as the default option and is checked during compilation to make the code more secure and reliable. 2294```cpp 2295class Foo; 2296 2297void PrintFoo(const Foo& foo); 2298``` 2299 2300### <a name="r9-6-2"></a>Rule 9.6.2 For member functions that do not modify member variables, use const. 2301Declare the member function as `const` whenever possible. The access function should always be const. So long as the function of a member is not modified, the function is declared with const. 2302When you need to modify data members in a virtual function, take all classes in the inheritance chain into account instead of only focusing on the implementation of a single class. 2303```cpp 2304class Foo { 2305public: 2306 2307 // ... 2308 2309 int PrintValue() const // const modifies member functions and does not modify member variables. 2310 { 2311 std::cout << value_ << std::endl; 2312 } 2313 2314 int GetValue() const // const modifies member functions and does not modify member variables. 2315 { 2316 return value_; 2317 } 2318 2319private: 2320 int value_; 2321}; 2322``` 2323 2324### <a name="a9-6-1"></a>Rec 9.6.1 Member variables that will not be modified after initialization should be defined as constants. 2325 2326```cpp 2327class Foo { 2328public: 2329 Foo(int length) : dataLength_(length) {} 2330private: 2331 const int dataLength_; 2332}; 2333``` 2334 2335## <a name="c9.7"></a> Exceptions 2336 2337### <a name="a9-7-1"></a>Rec 9.7.1 If the function does not throw an exception, the declaration is `noexcept`. 2338**Reasons:** 23391. If the function does not throw an exception, the declaration is `noexcept`, which enables the compiler to optimize the function to the maximum extent, for example, reducing the execution paths and improving the efficiency of exiting when an error occurs. 23402. For STL containers such as `vector`, to ensure the interface robustness, if the `move ` constructor of saved items is not declared as `noexcept`, the `copy machanism` instead of the `move machanism` is used when the items are removed from the container. This would cause performance loss risks. If the function does not throw an exception, or a program does not intercept and process an exception thrown by the function, new `noexcept` keywords can be used to modify the function, indicating that the function does not throw an exception or the thrown exception is not intercepted or processed. For example: 2341 2342```cpp 2343extern "C" double sqrt(double) noexcept; // No exceptions are thrown. 2344 2345// noexcept can still be used when exceptions may be thrown. 2346// The exception of memory exhaustion is not processed. The function is simply declared as noexcept. 2347std::vector<int> MyComputation(const std::vector<int>& v) noexcept 2348{ 2349 std::vector res = v; // Exceptions may be thrown. 2350 // do something 2351 return res; 2352} 2353``` 2354 2355**Example:** 2356 2357```cpp 2358RetType Function(Type params) noexcept; // Maximized optimization 2359RetType Function(Type params) noexcept; // No optimization 2360 2361// Declaration as noexcept for the move operation of std::vector is needed. 2362class Foo1 { 2363public: 2364 Foo1(Foo1&& other); // no noexcept 2365}; 2366 2367std::vector<Foo1> a1; 2368a1.push_back(Foo1()); 2369a1.push_back(Foo1()); // The copy constructor is called to enable the container expansion and removal of existing items. 2370 2371class Foo2 { 2372public: 2373 Foo2(Foo2&& other) noexcept; 2374}; 2375 2376std::vector<Foo2> a2; 2377a2.push_back(Foo2()); 2378a2.push_back(Foo2()); //Triggers container expansion and invokes the move constructor to move existing elements. 2379``` 2380 2381**Note** 2382The default constructor, destructor, `swap` function, and `move` operator should not throw an exception. 2383 2384## <a name="c9-8"></a>Templates and Generic Programming 2385 2386### <a name="a9-8-1"></a>Rule 9.8.1 Do not use generic programming. 2387OpenHarmony adopts object-oriented programming, which has ideas, concepts, and techniques totally different from those of generic programming. 2388 2389Generic programming in C++ allows for extremely flexible interfaces that are type safe and high performance, enabling reuse of code of different types but with the same behavior. 2390 2391However, generic programming has the following disadvantages: 2392 23931. People who are not familiar with generic programming often write into templates object-oriented logic or members that do not depend on template parameters, making the code unreadable or bloated. 23942. The techniques used in generic programming are often obscure to anyone but language experts. Template code can be unreadable in certain cases. 23953. Generic programming often leads to extremely poor compile time error messages. The uncalled-for implementation details of APIs are displayed to users in the error messages, making even a simple API difficult to debug. 23964. Inappropriate use of templates cause code bloat during runtime. 23975. It is difficult to modify or refactor the template code. The template code is expanded in multiple contexts, and it is hard to verify that the code refactoring makes sense in all of them. 2398 2399Only __a few components of OpenHarmony__ support generic programming, and the templates developed using generic programming must have detailed comments. 2400Exceptions: 24011. The STL adaptation layer can use generic programming. 2402 2403## <a name="c9-9"></a>Macros 2404In the C++ language, it is strongly recommended that complex macros be used as little as possible. 2405- For constant definitions, use `const` or `enum` as stated in the preceding sections. 2406- For macro functions, try to be as simple as possible, comply with the following principles, and use inline functions and template functions for replacement. 2407 2408```cpp 2409// The macro function is not recommended. 2410#define SQUARE(a, b) ((a) * (b)) 2411 2412// Use the template function and inline function as a replacement. 2413template<typename T> T Square(T a, T b) { return a * b; } 2414``` 2415 2416For details about how to use macros, see the related chapters about the C language specifications. 2417**Exception**: For some common and mature applications, for example, encapsulation for new and delete, the use of macros can be retained. 2418 2419# <a name="c10"></a> 10 Modern C++ Features 2420 2421As the ISO released the C++ 11 language standard in 2011 and released the C++ 17 in March 2017, the modern C++ (C++ 11/14/17) adds a large number of new language features and standard libraries that improve programming efficiency and code quality. 2422This chapter describes some guidelines for modern C++ use, to avoid language pitfalls. 2423 2424## <a name="c10-1"></a> Code Simplicity and Security Improvement 2425### <a name="a10-1-1"></a>Rec 10.1.1 Use `auto` properly. 2426**Reasons** 2427 2428* `auto` can help you avoid writing verbose, repeated type names, and can also ensure initialization when variables are defined. 2429* The `auto` type deduction rules are complex and need to be read carefully. 2430* If using `auto` makes the code clearer, use a specific type of it and use it only for local variables. 2431 2432**Example** 2433 2434```cpp 2435// Avoid verbose type names. 2436std::map<string, int>::iterator iter = m.find(val); 2437auto iter = m.find(val); 2438 2439// Avoid duplicate type names. 2440class Foo {...}; 2441Foo* p = new Foo; 2442auto p = new Foo; 2443 2444// Ensure that the initialization is successful. 2445int x; // The compilation is correct but the variable is not initialized. 2446auto x; // The compilation failed. Initialization is needed. 2447``` 2448 2449`auto` type deduction may cause the following problems: 2450 2451```cpp 2452auto a = 3; // int 2453const auto ca = a; // const int 2454const auto& ra = a; // const int& 2455auto aa = ca; // int, const and reference are neglected. 2456auto ila1 = { 10 }; // std::initializer_list<int> 2457auto ila2{ 10 }; // std::initializer_list<int> 2458 2459auto&& ura1 = x; // int& 2460auto&& ura2 = ca; // const int& 2461auto&& ura3 = 10; // int&& 2462 2463const int b[10]; 2464auto arr1 = b; // const int* 2465auto& arr2 = b; // const int(&)[10] 2466``` 2467 2468If you do not pay attention to `auto` type deduction and ignore the reference, hard-to-find performance problems may be created. 2469 2470```cpp 2471std::vector<std::string> v; 2472auto s1 = v[0]; // auto deduction changes s1 to std::string in order to copy v[0]. 2473``` 2474 2475If `auto` is used to define an interface, such as a constant in a header file, the type may be changed if the developer has modified the value. 2476 2477### <a name="r10-1-1"></a>Rule 10.1.1 Use the keyword `override` when rewriting virtual functions. 2478**Reason:** 2479The keyword `override` ensures that the function is a virtual function and an overridden virtual function of the base class. If the subclass function is different from the base class function prototype, a compilation alarm is generated. `final` also ensures that virtual functions are not overridden by subclasses. 2480 2481If you modify the prototype of a base class virtual function but forget to modify the virtual function overridden by the subclass, you can find inconsistency during compilation. You can also avoid forgetting to modify the overridden function when there are multiple subclasses. 2482 2483**Example** 2484 2485```cpp 2486class Base { 2487public: 2488 virtual void Foo(); 2489 virtual void Foo(int var); 2490 void Bar(); 2491}; 2492 2493class Derived : public Base { 2494public: 2495 void Foo() const override; // Compilation failed: derived::Foo is different from that of the prototype of base::Foo and is not overridden. 2496 void Foo() override; // Compilation successful: derived::Foo overrode base::Foo. 2497 void Foo(int var) final; // Compilation successful: Derived::Foo(int) rewrites Base::Foo(int), and the derived class of Derived cannot override this function. 2498 void Bar() override; // Compilation failed: base::Bar is not a virtual function. 2499}; 2500``` 2501 2502**Summary** 25031. When defining the virtual function for the first time based on the base class, use the keyword `virtual`. 25042. When overriding the virtual function by a subclass in a base class, including destructors, use the keyword `override` or `final` instead of `virtual`. 25053. For the non-virtual function, do not use `virtual` or `override`. 2506 2507### <a name="r10-1-2"></a>Rule: 10.1.2 Use the keyword `delete` to delete functions. 2508**Reason** 2509The `delete` keyword is clearer and the application scope is wider than a class member function that is declared as private and not implemented. 2510 2511**Example:** 2512 2513```cpp 2514class Foo { 2515private: 2516 // Whether the copy structure is deleted or not is unknown because usually only the header file is checked. 2517 Foo(const Foo&); 2518}; 2519 2520class Foo { 2521public: 2522 // Explicitly delete the copy assignment operator. 2523 Foo& operator=(const Foo&) = delete; 2524}; 2525``` 2526 2527The `delete` keyword can also be used to delete non-member functions. 2528 2529```cpp 2530template<typename T> 2531void Process(T value); 2532 2533template<> 2534void Process<void>(void) = delete; 2535``` 2536 2537### <a name="r10-1-3"></a>Rule 10.1.3 Use `nullptr` instead of `NULL` or `0`. 2538**Reason:** 2539For a long time, C++ has not had a keyword that represents a null pointer, which is embarrassing: 2540 2541```cpp 2542#define NULL ((void *)0) 2543 2544char* str = NULL; // Error: void* cannot be automatically converted to char*. 2545 2546void(C::*pmf)() = &C::Func; 2547if (pmf == NULL) {} // Error: void* cannot be automatically converted to the pointer that points to the member function. 2548``` 2549 2550If `NULL` is defined as `0` or `0L`, the above problems can be solved. 2551 2552Alternatively, use `0` directly in places where null pointers are required. However, another problem occurs. The code is not clear, especially when `auto` is used for automatic deduction. 2553 2554```cpp 2555auto result = Find(id); 2556if (result == 0) { // Does Find() return a pointer or an integer? 2557 // do something 2558} 2559``` 2560 2561Literally `0` is of the `int` type (`0L` is the `long` type). Therefore, neither `NULL` nor `0` is a pointer type. 2562When a function of the pointer or integer type is overloaded, `NULL` or `0` calls only the overloaded pointer function. 2563 2564```cpp 2565void F(int); 2566void F(int*); 2567 2568F(0); // Call F(int) instead of F(int*). 2569F(NULL); // Call F(int) instead of F(int*). 2570``` 2571 2572In addition, `sizeof(NULL) == sizeof(void*)` does not always make sense, which is a potential risk. 2573 2574Summary: If `0` or `0L` is directly used, the code is not clear and type security cannot be ensured. If `NULL` is used, the type security cannot be ensured. These are all potential risks. 2575 2576`nullptr` has many advantages. It literally represents the null pointer and makes the code clearer. More to the point, it is no longer an integer type. 2577 2578`nullptr` is of the `std::nullptr_t` type. `std::nullptr_t` can be implicitly converted into all original pointer types, so that `nullptr` can represent a null pointer that points to any type. 2579 2580```cpp 2581void F(int); 2582void F(int*); 2583F(nullptr); // Call F(int*). 2584 2585auto result = Find(id); 2586if (result == nullptr) { // Find() returns a pointer. 2587 // do something 2588} 2589``` 2590 2591### <a name="r10-1-4"></a>Rule 10.1.4 Use `using` instead of `typedef`. 2592For versions earlier than `C++11`, you can define the alias of the type by using `typedef`. No one wants to repeat code like `std::map<uint32_t, std::vector<int>>`. 2593 2594```cpp 2595typedef std::map<uint32_t, std::vector<int>> SomeType; 2596``` 2597 2598Using alias for the type is actually encapsulating the type. This encapsulation makes the code clearer, and to a large extent avoids the bulk modification caused by the type change. 2599For versions supporting C++ 11 features, `using` is provided to implement `alias declarations`: 2600 2601```cpp 2602using SomeType = std::map<uint32_t, std::vector<int>>; 2603``` 2604 2605Compare the two formats: 2606 2607```cpp 2608typedef Type Alias; // It cannot be told whether the original Type or Alias is at the front. 2609using Alias = Type; // The format confirms to the assignment rule. It is easy to understand and helps reduce errors. 2610``` 2611 2612If this is not enough to prove the advantages of `using`, the alias template may be a better example: 2613 2614```cpp 2615//: Only one line of code is need to define an alias for a template. 2616template<class T> 2617using MyAllocatorVector = std::vector<T, MyAllocator<T>>; 2618 2619MyAllocatorVector data; // An alias for a template defined with "using". 2620 2621template<class T> 2622class MyClass { 2623private: 2624 MyAllocatorVector data_; // Another. 2625}; 2626``` 2627 2628`typedef` does not support alias templates and they have to be hacked in. 2629 2630```cpp 2631// A template is used for packaging typedef. Therefore, a template class is needed. 2632template<class T> 2633struct MyAllocatorVector { 2634 typedef std::vector<T, MyAllocator<T>> type; 2635}; 2636 2637MyAllocatorVector::type data; // ::type needs to be added when using typedef to define an alias. 2638 2639template<class T> 2640class MyClass { 2641private: 2642 typename MyAllocatorVector::type data_; // For a template class, typename is also needed in addition to ::type. 2643}; 2644``` 2645 2646### <a name="r10-1-5"></a>Rule 10.1.5 Do not use std::move to operate the const object. 2647Literally, `std::move` means moving an object. The const object cannot be modified and cannot be moved. Therefore, using `std::move` to operate the const object may confuse code readers. 2648Regarding actual functions, `std::move` converts an object to the rvalue reference type. It can convert the const object to the rvalue reference of const. Because few types define the move constructor and the move assignment operator that use the const rvalue reference as the parameter, the actual function of code is often degraded to object copy instead of object movement, which brings performance loss. 2649 2650**Bad example:** 2651```cpp 2652std::string g_string; 2653std::vector<std::string> g_stringList; 2654 2655void func() 2656{ 2657 const std::string myString = "String content"; 2658 g_string = std::move(myString); // Bad: myString is not moved. Instead, it is copied. 2659 const std::string anotherString = "Another string content"; 2660 g_stringList.push_back(std::move(anotherString)); // Bad: anotherString is not moved. Instead, it is copied. 2661} 2662``` 2663 2664## <a name="c10-2"></a> Smart Pointers 2665### <a name="r10-2-1"></a>Rule 10.2.1 Preferentially use the original pointer instead of the smart pointer for singletons and class members that are not held by multiple parties. 2666**Reason:** 2667Smart pointers automatically release object resources to prevent resource leakage, but they require extra resource overheads. For example, the classes, constructors, and destructors automatically generated by smart pointers consume more resources such as memory. 2668 2669When singletons, class members, and the like are not held by multiple parties, resources can be released only in class destructors. In this case, smart pointers should not be used. 2670 2671**Example:** 2672 2673```cpp 2674class Foo; 2675class Base { 2676public: 2677 Base() {} 2678 virtual ~Base() 2679 { 2680 delete foo_; 2681 } 2682private: 2683 Foo* foo_ = nullptr; 2684}; 2685``` 2686 2687**Exceptions** 26881. When a created object is returned, a smart pointer can be used if the pointer destructor is required. 2689```cpp 2690class User; 2691class Foo { 2692public: 2693 std::unique_ptr<User, void(User *)> CreateUniqueUser() // Use unique_ptr to ensure that the object is created and released in the same runtime. 2694 { 2695 sptr<User> ipcUser = iface_cast<User>(remoter); 2696 return std::unique_ptr<User, void(User *)>(::new User(ipcUser), [](User *user) { 2697 user->Close(); 2698 ::delete user; 2699 }); 2700 } 2701 2702 std::shared_ptr<User> CreateSharedUser() // Use shared_ptr to ensure that the object is created and released in the same runtime. 2703 { 2704 sptr<User> ipcUser = iface_cast<User>(remoter); 2705 return std::shared_ptr<User>(ipcUser.GetRefPtr(), [ipcUser](User *user) mutable { 2706 ipcUser = nullptr; 2707 }); 2708 } 2709}; 2710``` 27112. When the created object is returned and needs to be referenced by multiple parties, `shared_ptr` can be used. 2712 2713### <a name="r10-2-2"></a>Rule 10.2.2 Use `unique_ptr` instead of `shared_ptr`. 2714**Reasons:** 27151. Using `shared_ptr` a lot has an overhead (atomic operations on the `shared_ptr`s reference count have a measurable cost). 27162. Shared ownership in some cases (such as circular dependency) may create objects that can never be released. 27173. Shared ownership can be an attractive alternative to careful ownership design but it may obfuscate the design of a system. 2718 2719### <a name="r10-2-2"></a>Rule 10.2.2 Use `std::make_unique` instead of `new` to create a `unique_ptr`. 2720**Reasons:** 27211. `make_unique` provides a simpler creation method. 27222. `make_unique` ensures the exception safety of complex expressions. 2723 2724**Example:** 2725 2726```cpp 2727// Bad: MyClass appears twice, which carries a risk of inconsistency. 2728std::unique_ptr<MyClass> ptr(new MyClass(0, 1)); 2729// Good: MyClass appears once and there is no possibility of inconsistency. 2730auto ptr = std::make_unique<MyClass>(0, 1); 2731``` 2732 2733Recurrence of types may cause serious problems, and it is difficult to find them: 2734 2735```cpp 2736// The code compiles fine, but new and delete usage does not match. 2737std::unique_ptr<uint8_t> ptr(new uint8_t[10]); 2738std::unique_ptr<uint8_t[]> ptr(new uint8_t); 2739// No exception safety: The compiler may calculate parameters in the following order: 2740// 1. Allocate the memory of Foo. 2741// 2. Construct Foo. 2742// 3. Call Bar. 2743// 4. Construct unique_ptr<Foo>. 2744// If Bar throws an exception, Foo is not destroyed and a memory leak occurs. 2745F(unique_ptr<Foo>(new Foo()), Bar()); 2746 2747// Exception safety: Calling of function is not interrupted. 2748F(make_unique<Foo>(), Bar()); 2749``` 2750 2751**Exception:** 2752`std::make_unique` does not support user-defined `deleter`. 2753In the scenario where the `deleter` needs to be customized, it is recommended that `make_unique` be implemented in the customized version's own namespace. 2754Using `new` to create `unique_ptr` with the user-defined `deleter` is the last choice. 2755 2756### <a name="r10-2-4"></a>Rule 10.2.4 Create `shared_ptr` by using `std::make_shared` instead of `new`. 2757**Reason:** 2758In addition to the consistency factor similar to that in `std::make_unique` when using `std::make_shared`, performance is also a factor to consider. 2759`std::shared_ptr` manages two entities: 2760* Control block (storing reference count, `deleter`, etc.) 2761* Managed objects 2762 2763When `std::make_shared` creates `std::shared_ptr`, it allocates sufficient memory for storing control blocks and managed objects on the heap at a time. When `std::shared_ptr<MyClass>(new MyClass)`is used to create a `std::shared_ptr`, not only does `new MyClass` trigger heap allocation, but the constructor function of `std::shard_ptr` triggers a second heap allocation, resulting in extra overhead. 2764 2765**Exception:** 2766Similar to `std::make_unique`, `std::make_shared` does not support `deleter` customization. 2767 2768## <a name="c10-3"></a> Lambda 2769### <a name="a10-3-1"></a>Rec 10.3.1 Use `lambda` to capture local variables or write local functions when normal functions do not work. 2770**Reason:** 2771Functions cannot capture local variables or be declared at local scope. If you need those things, choose `lambda` instead of handwritten `functor`. 2772On the other hand, `lambda` and `functor` objects do not support overloading. If overloading is required, use a function. 2773If both `lambda` and functions work, a function is preferred. Use the simplest tool. 2774 2775**Example:** 2776 2777```cpp 2778// Write a function that accepts only an int or string. 2779// -- Overloading is more natural. 2780void F(int); 2781void F(const string&); 2782 2783// The local state needs to be captured or appear in the statement or expression range. 2784// -- A lambda is more natural. 2785vector<Work> v = LotsOfWork(); 2786for (int taskNum = 0; taskNum < max; ++taskNum) { 2787 pool.Run([=, &v] {...}); 2788} 2789pool.Join(); 2790``` 2791 2792### <a name="r10-3-1"></a>Rule 10.3.1 Avoid capturing by reference in lambdas that will not be used locally. 2793**Reason:** 2794Using `lambdas` at a "nonlocal" scope includes returning, storing on the heap, and passing to another thread. Local pointers and references should not outlive their scope. Capturing by reference in `lambdas` indicates storing a reference to a local object. If this leads to a reference that exceeds the lifecycle of a local variable, capturing by reference should not be used. 2795 2796**Example:** 2797 2798```cpp 2799// Bad 2800void Foo() 2801{ 2802 int local = 42; 2803 // Capture a reference to a local variable. 2804 // After the function returns results, local no longer exists, 2805 // Process() call will have undefined behavior. 2806 threadPool.QueueWork([&]{ Process(local); }); 2807} 2808 2809// Good 2810void Foo() 2811{ 2812 int local = 42; 2813 // Capture a copy of local. 2814 // Since a copy of local is made, it will be always available for the call. 2815 threadPool.QueueWork([=]{ Process(local); }); 2816} 2817``` 2818 2819### <a name="a10-3-2"></a>Rec 10.3.2 All variables are explicitly captured if `this` is captured. 2820**Reason:** 2821The `[=]` in the member function seems to indicate capturing by value but actually it is capturing data members by reference because it captures the invisible `this` pointer by value. Generally, it is recommended that capturing by reference be avoided. If it is necessary to do so, write `this` explicitly. 2822 2823**Example:** 2824 2825```cpp 2826class MyClass { 2827public: 2828 void Foo() 2829 { 2830 int i = 0; 2831 2832 auto Lambda = [=]() { Use(i, data_); }; // Bad: It looks like we are copying or capturing by value but member variables are actually captured by reference. 2833 2834 data_ = 42; 2835 Lambda(); // Call use(42); 2836 data_ = 43; 2837 Lambda(); // Call use(43); 2838 2839 auto Lambda2 = [i, this]() { Use(i, data_); }; // Good: the most explicit and least confusing method. 2840 } 2841 2842private: 2843 int data_ = 0; 2844}; 2845``` 2846 2847### <a name="a10-3-3"></a>Rec 10.3.3 Avoid default capture modes. 2848**Reason:** 2849The lambda expression provides two default capture modes: by-reference (&) and by-value (=). 2850By default, the "by-reference" capture mode will implicitly capture the reference of all local variables, which will easily lead to dangling references. By contrast, explicitly writing variables that need to be captured can make it easier to check the lifecycle of an object and reduce the possibility of making a mistake. 2851By default, the "by-value" capture mode will implicitly capture this pointer, and it is difficult to find out which variables the lambda function depends on.If a static variable exists, the reader mistakenly considers that the lambda has copied a static variable. 2852Therefore, it is required to clearly state the variables that lambda needs to capture, instead of using the default capture mode. 2853 2854**Bad example:** 2855```cpp 2856auto func() 2857{ 2858 int addend = 5; 2859 static int baseValue = 3; 2860 2861 return [=]() { // Only addend is actually copied. 2862 ++baseValue; // The modification will affect the value of the static variable. 2863 return baseValue + addend; 2864 }; 2865} 2866``` 2867 2868**Good example:** 2869```cpp 2870auto func() 2871{ 2872 int addend = 5; 2873 static int baseValue = 3; 2874 2875 return [addend, baseValue = baseValue]() mutable { // Uses the C++14 capture initialization to copy a variable. 2876 ++baseValue; // Modifying the copy of a static variable does not affect the value of the static variable. 2877 return baseValue + addend; 2878 }; 2879} 2880``` 2881 2882Reference: Effective Modern C++: Item 31: Avoid default capture modes. 2883 2884## <a name="c10-4"></a> Interfaces 2885### <a name="a10-4-1"></a>Rec 10.4.1 Use `T*` or `T&` arguments instead of a smart pointer in scenarios where ownership is not involved. 2886**Reasons:** 28871. Passing a smart pointer to transfer or share ownership should only be used when the ownership mechanism is explicitly required. 28882. Passing a smart pointer (for example, passing the `this` smart pointer) restricts the use of a function to callers using smart pointers. 28893. Passing a shared smart pointer adds a runtime performance cost. 2890 2891**Example:** 2892 2893```cpp 2894// Accept any int*. 2895void F(int*); 2896 2897// Accept only integers for which you want to transfer ownership. 2898void G(unique_ptr<int>); 2899 2900// Accept only integers for which you want to share ownership. 2901void G(shared_ptr<int>); 2902 2903// Does not need to change the ownership but requires ownership of the caller. 2904void H(const unique_ptr<int>&); 2905 2906// Accept any int. 2907void H(int&); 2908 2909// Bad 2910void F(shared_ptr<Widget>& w) 2911{ 2912 // ... 2913 Use(*w); // When only w is used, lifecycle management is not required. 2914 // ... 2915}; 2916``` 2917