OpenHarmony-cpp-coding-style-guide.md 118.2 KB
Newer Older
N
NEEN 已提交
1 2
# C++ Coding Style Guide

G
Gloria 已提交
3
## Purpose
W
wusongqing 已提交
4
Rules 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.
G
Gloria 已提交
5

W
wusongqing 已提交
6
Before referring to this coding style guide, you are expected to have the following basic capabilities of the C++programming language: 
G
Gloria 已提交
7

N
NEEN 已提交
8 9
1. Have a general knowledge of ISO standards for C++.
2. Be familiar with the basic features of C++, including those of C++ 03/11/14/17.
W
wusongqing 已提交
10
3. Have a general knowledge of the C++ standard library.
N
NEEN 已提交
11

G
Gloria 已提交
12
## General Principles
N
NEEN 已提交
13 14
Code must meet the requirements for readability, maintainability, security, reliability, testability, efficiency, and portability while ensuring functionality correctness.  

G
Gloria 已提交
15
## Key Points
N
NEEN 已提交
16 17 18 19
1. C++ programming style, such as naming and typesetting.
2. C++ modular design, including how to design header files, classes, interfaces, and functions.
3. Best practices of C++ features, including constants, type casting, resource management, and templates.
4. Best practices of modern C++, including conventions that can improve code maintainability and reliability in C++ 11/14/17.
W
wusongqing 已提交
20
5. This coding style guide is preferentially applicable to C++17.
N
NEEN 已提交
21

G
Gloria 已提交
22
## Conventions
W
wusongqing 已提交
23
**Rule**: Conventions that must be followed during programming.
N
NEEN 已提交
24

W
wusongqing 已提交
25
**Rec**: Conventions that must be considered during programming.
N
NEEN 已提交
26

W
wusongqing 已提交
27
This document is applicable to standard C++ versions (C++ 03/11/14/17) unless otherwise specified.
N
NEEN 已提交
28

G
Gloria 已提交
29
## Exceptions
W
wusongqing 已提交
30
It is necessary to understand the reason for these conventions and try to comply with them, no matter if they are rules or recommendations.
G
Gloria 已提交
31

N
NEEN 已提交
32 33 34
However, some rules and recommendations have exceptions.

The only acceptable exceptions are those that do not violate the general principles and provide appropriate reasons for the exception.
G
Gloria 已提交
35

N
NEEN 已提交
36 37
Try to avoid exceptions because they affect the code consistency. Exceptions to 'Rules' should be very rare.

G
Gloria 已提交
38 39
The style consistency principle is preferred in the following case: 

W
wusongqing 已提交
40
When you modify open-source or third-party code, comply with their respective code specifications.
N
NEEN 已提交
41

G
Gloria 已提交
42 43
# 2 Naming
## General Naming Rules
N
NEEN 已提交
44
__CamelCase__
W
wusongqing 已提交
45
CamelCase 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.
G
Gloria 已提交
46

W
wusongqing 已提交
47
There are two conventions: UpperCamelCase and lowerCamelCase.
N
NEEN 已提交
48 49 50 51


| Type                                     | Naming Style                             |
| ---------------------------------------- | ---------------------------------------- |
W
wusongqing 已提交
52
| Class, struct, enumeration, union, scope name| UpperCamelCase                           |
W
wusongqing 已提交
53
| Functions (including global functions, scope functions, and member functions) | UpperCamelCase                           |
W
wusongqing 已提交
54 55
| 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                           |
| Macro, constant, enumerated value, goto tag| All capitalized, separated by underscores (\_)|
N
NEEN 已提交
56 57

Note:
G
Gloria 已提交
58

W
wusongqing 已提交
59
**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.
G
Gloria 已提交
60

N
NEEN 已提交
61 62
**Variable** indicates the variables excluding those defined in **Constant**. These variables use the lowerCamelCase style.

G
Gloria 已提交
63 64
## File Naming
### Rule 2.2.1 Use .cpp as the C++ file name extension and .h as the header file name extension.
W
wusongqing 已提交
65
It 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++.
G
Gloria 已提交
66

W
wusongqing 已提交
67
It 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.
N
NEEN 已提交
68 69 70 71 72 73 74

At present, there are some other file name extensions used by programmers:

- Header files:  .hh, .hpp, .hxx
- Implementation files: .cc, .cxx, .C

If your project team uses a specific file name extension, you can continue to use it and keep the style consistent.
G
Gloria 已提交
75

N
NEEN 已提交
76 77 78
This document uses .h and .cpp extensions.


G
Gloria 已提交
79
### Rule 2.2.2 Keep C++ file names the same as the class name.
W
wusongqing 已提交
80
The 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.
N
NEEN 已提交
81 82 83 84 85 86 87

For example, if there is a class named DatabaseConnection, the corresponding file names are as follows:
- database_connection.h
- database_connection.cpp

The naming rules of struct, namespace, and enumeration definition files are similar to the rules above.

G
Gloria 已提交
88
## Function Naming
W
wusongqing 已提交
89
Functions are named in the UpperCamelCase style. Generally, the verb or verb-object structure is used.
N
NEEN 已提交
90 91 92 93 94 95 96 97 98 99 100 101 102
```cpp
class List {
public:
	void AddElement(const Element& element);
	Element GetElement(const unsigned int index) const;
	bool IsEmpty() const;
};

namespace Utils {
    void DeleteUser();
}
```

G
Gloria 已提交
103
## Type Naming
N
NEEN 已提交
104 105

Types are named in the UpperCamelCase style.
G
Gloria 已提交
106

W
wusongqing 已提交
107
All types, such as classes, structs, unions, typedefs, and enumerations, use the same conventions. Example:
G
Gloria 已提交
108

N
NEEN 已提交
109
```cpp
W
wusongqing 已提交
110
// Classes, structs, and unions
N
NEEN 已提交
111 112 113 114 115 116 117 118
class UrlTable { ...
class UrlTableTester { ...
struct UrlTableProperties { ...
union Packet { ...

// typedefs
typedef std::map<std::string, UrlTableProperties*> PropertiesMap;

W
wusongqing 已提交
119
// Enums
N
NEEN 已提交
120 121 122 123 124
enum UrlTableErrors { ...
```

For namespace naming, UpperCamelCase is recommended.
```cpp
W
wusongqing 已提交
125
// Namespaces
N
NEEN 已提交
126 127 128 129 130 131 132 133 134 135
namespace OsUtils {
  
namespace FileUtils {
      
}
  
}
```


G
Gloria 已提交
136
### Rec 2.4.1 Do not abuse typedef or #define to set alias for the basic data types.
W
wusongqing 已提交
137
Unless otherwise specified, do not use typedef or #define to redefine basic data types.
G
Gloria 已提交
138

W
wusongqing 已提交
139
The basic data types found in the `<cstdint>` header file are preferable.
N
NEEN 已提交
140 141 142 143 144 145 146 147 148 149

| Signed Type | Unsigned Type | Description                              |
| ----------- | ------------- | ---------------------------------------- |
| int8_t      | uint8_t       | The signed or unsigned 8-bit integer type. |
| int16_t     | uint16_t      | The signed or unsigned 16-bit integer type. |
| int32_t     | uint32_t      | The signed or unsigned 32-bit integer type. |
| int64_t     | uint64_t      | The signed or unsigned 64-bit integer type. |
| intptr_t    | uintptr_t     | The signed or unsigned integer type large enough to hold a pointer. |


G
Gloria 已提交
150
## Variable Naming
W
wusongqing 已提交
151
General variables, including global variables, function parameters, local variables, and member variables, are named in the lowerCamelCase style.
N
NEEN 已提交
152 153 154 155 156 157
```cpp
std::string tableName;  // Good: Recommended style.
std::string tablename;  // Bad: Forbidden style.
std::string path;       // Good: When there is only one word, lowerCamelCase (all lowercase) is used.
```

G
Gloria 已提交
158
### Rule 2.5.1 Add the prefix 'g_' to global variables. Do not add a prefix to a static variable.
W
wusongqing 已提交
159
Global 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.
N
NEEN 已提交
160
- Global static variables and global variables are named in the same way.
W
wusongqing 已提交
161 162
- Static variables in functions and common local variables are named in the same way.
- Static member variables in classes and common member variables are named in the same way.
N
NEEN 已提交
163 164 165 166 167 168 169 170 171 172 173

```cpp
int g_activeConnectCount;

void Func()
{
    static int packetCount = 0;  
    ...
}
```

G
Gloria 已提交
174
### Rule 2.5.2 Name member variables in classes in the unix\_like style.
N
NEEN 已提交
175 176 177 178

```cpp
class Foo {
private:
W
wusongqing 已提交
179
    std::string fileName_;   // Add the underscore (\_) as the suffix, similar to the K&R naming style.
N
NEEN 已提交
180 181 182 183
};
```
Use 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.

G
Gloria 已提交
184
## Macro, Constant, and Enum Naming
W
wusongqing 已提交
185 186
Use uppercase letters separated by underscores (\_) for macro names and enumerated values.
In 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.
N
NEEN 已提交
187 188

```cpp
W
wusongqing 已提交
189
#define MAX(a, b) (((a) < (b))? (b): (a)) // Example of naming a macro only.
N
NEEN 已提交
190

W
wusongqing 已提交
191
enum BaseColor {    // Note: The enum type name is in the UpperCamelCase style, whereas the enumerated value is in uppercase letters separated by underscores (\_).
N
NEEN 已提交
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
    RED,
    DARK_RED,
    GREEN,
    LIGHT_GREEN
};

int Func(...)
{
    const unsigned int bufferSize = 100;    // Local variable
    char *p = new char[bufferSize];
    ...
}

namespace Utils {
	const unsigned int DEFAULT_FILE_SIZE_KB = 200;        // Global variable
}

```

G
Gloria 已提交
211
# 3 Formatting
N
NEEN 已提交
212

G
Gloria 已提交
213
## Line Length
N
NEEN 已提交
214

G
Gloria 已提交
215
### Rule 3.1.1 Include 120 characters or less in each line.
W
wusongqing 已提交
216
If the line of code exceeds the permitted length, wrap the line appropriately.
N
NEEN 已提交
217

W
wusongqing 已提交
218 219 220
Exceptions:
- 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.
- The #include and #error statements are allowed to exceed the line length requirement. However, you should try to avoid this.
N
NEEN 已提交
221 222 223 224 225 226 227 228
- The error information in preprocessor directives can exceed the permitted length.
  Put the error information of preprocessor directives in one line to facilitate reading and understanding even if the line contains more than 120 characters.
```cpp
#ifndef XXX_YYY_ZZZ
#error Header aaaa/bbbb/cccc/abc.h must only be included after xxxx/yyyy/zzzz/xyz.h, because xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#endif
```

G
Gloria 已提交
229
## Indentation
N
NEEN 已提交
230

G
Gloria 已提交
231
### Rule 3.2.1 Use spaces to indent and indent 4 spaces at a time.
N
NEEN 已提交
232
Only spaces can be used for indentation. Four spaces are indented each time. Do not use the Tab character to indent.
G
Gloria 已提交
233

W
wusongqing 已提交
234
Currently, almost all integrated development environments (IDEs) support automatic conversion of a Tab input to four spaces. Configure your IDE to support indentation with spaces.
N
NEEN 已提交
235

G
Gloria 已提交
236 237
## Braces
### Rule 3.3.1 Use the K&R indentation style.
N
NEEN 已提交
238
__K&R style__
G
Gloria 已提交
239

W
wusongqing 已提交
240
While 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.
G
Gloria 已提交
241

W
wusongqing 已提交
242
The 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.
N
NEEN 已提交
243

W
wusongqing 已提交
244
Example:
N
NEEN 已提交
245
```cpp
W
wusongqing 已提交
246
struct MyType {     // The left brace is placed at the end of the line along with the statement, and one space is used for indentation.
N
NEEN 已提交
247 248 249 250
    ...
};

int Foo(int a)
W
wusongqing 已提交
251
{                   // The left brace of the function starts a new line, and nothing else is placed on the line.
N
NEEN 已提交
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
    if (...) {
        ...
    } else {
        ...
    }
}
```
The reasons for recommending this style are as follows:

- Code is more compact.
- Placing the brace at the end of the statement makes the code more continuous in reading rhythm than starting a new line.
- This style complies with mainstream norms and habits of programming languages.
- Most modern IDEs have an automatic code indentation, alignment and display. Placing the brace at the end of a line does not impact understanding.


If no function body is inside the braces, the braces can be put on the same line.
```cpp
class MyClass {
public:
    MyClass() : value_(0) {}
    
private:
    int value_;
};
```

G
Gloria 已提交
278
## Function Declarations and Definitions
N
NEEN 已提交
279

G
Gloria 已提交
280 281
### 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.
When 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.
W
wusongqing 已提交
282
The left parenthesis of a parameter list is always in the same line as the function name. The right parenthesis always follows the last parameter.
N
NEEN 已提交
283

W
wusongqing 已提交
284
Example:
N
NEEN 已提交
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
```cpp
ReturnType FunctionName(ArgType paramName1, ArgType paramName2)   // Good: All are in the same line.
{
    ...
}

ReturnType VeryVeryVeryLongFunctionName(ArgType paramName1,     // Each added parameter starts on a new line because the line length limit is exceeded.
                                        ArgType paramName2,     // Good: aligned with the previous parameter.
                                        ArgType paramName3)
{
    ...
}

ReturnType LongFunctionName(ArgType paramName1, ArgType paramName2, // Parameters are wrapped because the line length limit is exceeded.
    ArgType paramName3, ArgType paramName4, ArgType paramName5)     // Good: After the line break, 4 spaces are used for indentation.
{
    ...
}

ReturnType ReallyReallyReallyReallyLongFunctionName(            // The line length cannot accommodate even the first parameter, and a line break is required.
    ArgType paramName1, ArgType paramName2, ArgType paramName3) // Good: After the line break, 4 spaces are used for indentation.
{
    ...
}
```

G
Gloria 已提交
311 312
## Function Calls
### 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.
N
NEEN 已提交
313
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.
G
Gloria 已提交
314

N
NEEN 已提交
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
The left parenthesis always follows the function name, and the right parenthesis always follows the last parameter.

The following are examples of proper line breaks:
```cpp
ReturnType result = FunctionName(paramName1, paramName2);   // Good: All function parameters are on one line.

ReturnType result = FunctionName(paramName1,
                                 paramName2,                // Good: aligned with the previous parameter
                                 paramName3);

ReturnType result = FunctionName(paramName1, paramName2, 
    paramName3, paramName4, paramName5);                    // Good: Parameters are wrapped. After the line break, 4 spaces are used for indentation.

ReturnType result = VeryVeryVeryLongFunctionName(           // The line length cannot accommodate even the first parameter, and a line break is required.
    paramName1, paramName2, paramName3);                    // After the line break, 4 spaces are used for indentation.
```

If some of the parameters called by a function are associated with each other, you can group them for better understanding.
```cpp
// 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.
int result = DealWithStructureLikeParams(left.x, left.y,     // A group of related parameters.
                                         right.x, right.y);  // Another group of related parameters.
```

G
Gloria 已提交
339
## if Statements
N
NEEN 已提交
340

G
Gloria 已提交
341
### Rule 3.6.1 Use braces to include an if statement.
N
NEEN 已提交
342 343 344 345 346 347 348 349 350 351 352 353
We require that all if statements use braces, even if there is only one statement.

Reasons:
- The logic is intuitive and easy to read.
- It is less prone to mistakes when new code is added to the existing if statement.
- 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).

```cpp
if (objectIsNotExist) {         // Good: Braces are added to a single-line conditional statement.
    return CreateNewObject();
}
```
G
Gloria 已提交
354
### Rule 3.6.2 Place if, else, and else if keywords on separate lines.
N
NEEN 已提交
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
If there are multiple branches in a conditional statement, they should be placed on separate lines.

Good example:

```cpp
if (someConditions) { 
    DoSomething();
    ...
} else {  // Good: Put the if and else keywords on separate lines.
    ...
}
```

Bad example:

```cpp
if (someConditions) { ... } else { ... } // Bad: The if and else keywords are put on the same line.
```

G
Gloria 已提交
374 375
## Loop Statements
### Rule 3.7.1 Use braces after loop statements.
N
NEEN 已提交
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
Similar 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.
```cpp
for (int i = 0; i < someRange; i++) {   // Good: Braces are used.
    DoSomething();
}
```
```cpp
while (condition) {} // Good: The while loop body is empty. Braces should be used.
```
```cpp
while (condition) {
    continue; // Good: The continue keyword highlights the end of the empty loop. Braces should be used.
}
```

Bad example:
```cpp
for (int i = 0; i < someRange; i++)
    DoSomething();      // Bad: Braces are mandatory.
```
```cpp
while (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.
```

G
Gloria 已提交
400 401
## Switch Statements
### Rule 3.8.1 Indent case and default in a switch statement with four spaces.
N
NEEN 已提交
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
The indentation style of the switch statement is as follows:
```cpp
switch (var) {
    case 0:             // Good: Indented
        DoSomething1(); // Good: Indented
        break;
    case 1: {           // Good: Braces are added.
        DoSomething2();
        break;
    }
    default:
        break;
}
```

```cpp
switch (var) {
case 0:                 // Bad: case is not indented.
    DoSomething();
    break;
default:                // Bad: default is not indented.
    break;
}
```

G
Gloria 已提交
427 428 429 430 431 432
## Expressions

### Rec 3.9.1 Keep a consistent line break style for expressions and ensure that operators are placed at the end of a line.
A 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.
N
NEEN 已提交
433 434

Placing these at the end of a line indicates that the operation is to be continued on the next line.
W
wusongqing 已提交
435
Example:
N
NEEN 已提交
436 437 438 439

// Assume that the first line exceeds the length limit.
```cpp
if (currentValue > threshold &&  // Good: After the line break, the logical-AND operators are placed at the end of the line.
L
liuhui 已提交
440
    someCondition) {
N
NEEN 已提交
441 442 443 444 445 446 447 448 449 450
    DoSomething();
    ...
}

int result = reallyReallyLongVariableName1 +    // Good
             reallyReallyLongVariableName2;
```
After an expression is wrapped, ensure that the lines are aligned appropriately or indented with 4 spaces. See the following example.

```cpp
L
liuhui 已提交
451 452
int sum = longVariableName1 + longVariableName2 + longVariableName3 +
    longVariableName4 + longVariableName5 + longVariableName6;         // Good: indented with 4 spaces
N
NEEN 已提交
453

L
liuhui 已提交
454 455
int sum = longVariableName1 + longVariableName2 + longVariableName3 +
          longVariableName4 + longVariableName5 + longVariableName6;   // Good: The lines are aligned.
N
NEEN 已提交
456
```
G
Gloria 已提交
457
## Variable Assignment
N
NEEN 已提交
458

G
Gloria 已提交
459
### Rule 3.10.1 Multiple variable definitions and assignment statements cannot be written on one line.
N
NEEN 已提交
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
Each line should have only one variable initialization statement. It is easier to read and understand.

```cpp
int maxCount = 10;
bool isCompleted = false;
```

Bad example:

```cpp
int maxCount = 10; bool isCompleted = false; // Bad: Multiple variable initialization statements must be separated on different lines. Each variable initialization statement occupies one line.
int x, y = 0;  // Bad: Multiple variable definitions must be separated on different lines. Each definition occupies one line.

int pointX;
int pointY;
...
pointX = 1; pointY = 2;  // Bad: Multiple variable assignment statements must be separated on different lines.
```
Exception: 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.

G
Gloria 已提交
480
## Initialization
N
NEEN 已提交
481 482
Initialization is applicable to structs, unions, and arrays.

G
Gloria 已提交
483
### Rule 3.11.1 When an initialization list is wrapped, ensure that the line after the break is indented and aligned properly.
N
NEEN 已提交
484 485 486 487 488 489 490 491 492 493
If a structure or array initialization list is wrapped, the line after the break is indented with four spaces.
Choose the wrap location and alignment style for best comprehension.

```cpp
const int rank[] = {
    16, 16, 16, 16, 32, 32, 32, 32,
    64, 64, 64, 64, 32, 32, 32, 32
};
```

G
Gloria 已提交
494 495
## Pointers and References
### Rec 3.12.1 The pointer type `*` follows a variable name or type. There can be only one space to the side of it.
N
NEEN 已提交
496 497 498 499 500 501 502 503 504 505 506 507 508 509
Pointer naming: There can be only one space next to `*`.
```cpp
int* p = NULL;  // Good
int *p = NULL;  // Good

int*p = NULL;   // Bad
int * p = NULL; // Bad
```

Exception: When a variable is modified by const or restrict, `*` cannot follow the variable or type.
```cpp
const char * const VERSION = "V100";
```

G
Gloria 已提交
510
### Rec 3.12.2 The reference type `&` follows a variable name or type. There can be only one space to the side of it.
N
NEEN 已提交
511 512 513 514 515 516 517 518
Reference naming: There can be only one space around `&`.
```cpp
int i = 8;

int& p = i;     // Good
int &p = i;     // Good
int*& rp = pi; // Good: The reference type `*&` follows the type.
int *&rp = pi; // Good: The reference type `*&` follows the variable name.
G
Gloria 已提交
519
int* &rp = pi; // Good: The pointer type `*` follows the type and the reference type `&` follows the variable name.
N
NEEN 已提交
520 521 522 523 524

int & p = i;    // Bad
int&p = i;      // Bad
```

G
Gloria 已提交
525 526
## Compilation Preprocessing
### 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.
W
wusongqing 已提交
527 528
The number sign (#) must be placed at the beginning of a line for compilation preprocessing, even if the code is embedded in the function body.

G
Gloria 已提交
529
### Rule 3.13.2 Do not use macros.
W
wusongqing 已提交
530 531 532 533 534 535 536 537
Macros 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.
In C++, there are many ways to avoid using macros:
- Use `const` or `enum` to define constants that are easy to understand.
- Use namespaces to avoid name conflicts.
- Use the `inline` function to avoid function call overhead.
- Use the `template` function to handle multiple types.
Macros can be used in scenarios such as header guards, conditional compilation, and logging.

G
Gloria 已提交
538
### Rule 3.13.3 Do not use macros to represent constants.
W
wusongqing 已提交
539 540
Macros 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.

G
Gloria 已提交
541
### Rule 3.13.4 Do not use function-like macros.
W
wusongqing 已提交
542
Before 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.
G
Gloria 已提交
543

W
wusongqing 已提交
544
The disadvantages of the function-like macro are as follows:
G
Gloria 已提交
545

W
wusongqing 已提交
546 547 548 549 550 551 552 553 554
- Function-like macros have no type check, which is not as strict as the function call check.
- If macro parameters are not evaluated during macro expansion, unexpected results may occur.
- A macro has no independent scope.
- There are high skill requirements on the proper use of macros (for example, the usage of `#` and wide use of parentheses), which reduces readability.
- Extensions of some macros can only be implemented by specific compilers in specific scenarios, such as `statement expression` of `gcc`, reducing the portability.
- 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.
- 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.

Unlike 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).
G
Gloria 已提交
555

W
wusongqing 已提交
556
In 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.
W
wusongqing 已提交
557

W
wusongqing 已提交
558 559 560 561 562 563
Inline functions have the advantages of both functions and macros:
- Strict type checking is performed for inline functions.
- Parameters are evaluated only once for inline functions.
- Inline functions are expanded in place and there is no overhead for function calls.
- Inline functions are better optimized than standard functions.
For performance-sensitive code, consider using inline functions instead of standard functions.
N
NEEN 已提交
564

W
wusongqing 已提交
565
Exceptions:
G
Gloria 已提交
566

W
wusongqing 已提交
567
In 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.
N
NEEN 已提交
568

G
Gloria 已提交
569 570
## Whitespace
### Rule 3.14.1 Ensure that horizontal spaces are used to highlight keywords and important information, and avoid unnecessary whitespace.
N
NEEN 已提交
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
Horizontal 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:

- Add spaces after keywords such as if, switch, case, do, while, and for.
- Do not add spaces after the left parenthesis or before the right parenthesis.
- For expressions enclosed by braces, either add a space on either side or avoid a space on either side.
- Do not add spaces after any unary operator (& * + - ~ !).
- Add a space to the left and right sides of each binary operator (= + -< > * /% | & ^ <= >= == !=).
- Add spaces to the left and right sides of a ternary operator (? :).
- Do not add spaces between a prefix or suffix increment (++) or decrement (--) operator and a variable.
- Do not add spaces before or after a struct member operator (. ->).
- Do not add spaces before commas. Add spaces after commas.
- Do not add spaces between a template or type conversion operator (<>) and a type.
- Do not add spaces before or after a domain operator (::).
- Determine whether to add spaces before and after a colon (:) based on the actual situation.

In normal cases:
```cpp
void Foo(int b) {  // Good: A space is added before the left brace.

int i = 0;  // Good: During variable initialization, there should be spaces before and after =. Do not leave a space before the semicolon.

int buf[BUF_SIZE] = {0}; // Good: Spaces are not added in braces.
```

Function definition and call:
```cpp
int result = Foo(arg1,arg2);
                    ^    // Bad: A space should be added after the comma.

int result = Foo( arg1, arg2 );
                 ^          ^  // Bad: Spaces should not be added after the left parenthesis or before the right parenthesis.
```

Pointer and Address Operator
```cpp
x = *p;     // Good: There is no space between the operator * and the pointer p.
p = &x;     // Good: There is no space between the operator & and the variable x.
x = r.y;    // Good: When a member variable is accessed through the operator (.), no space is added.
x = r->y;   // Good: When a member variable is accessed through the operator (->), no space is added.
```

Other Operators:
```cpp
x = 0;   // Good: There is a space before and after the assignment operator (=).
x = -5;  // Good: There is no space between the minus sign (–) and the number.
++x;     //Good: Do not add spaces between a prefix or suffix increment (++) or decrement (--) operator and a variable..
x--;

if (x && !y) // Good: There is a space before and after the Boolean operator. There is no space between the ! operator and the variable.
v = w * x + y / z;  // Good: There is a space before and after the binary operator.
v = w * (x + z);    // Good: There is no space before or after the expression in the parentheses.

int a = (x < y) ? x : y;  // Good: Ternary operator. There is a space before and after ? and :
```

Loops and Conditional Statements:
```cpp
if (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.
    ...
} else {           // Good: There is a space between the else keyword and the left brace.
    ...
}

while (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.

for (int i = 0; i < someRange; ++i) {  // Good: There is a space between the for keyword and the left parenthesis, and after the semicolon.
    ...
}

switch (condition) {  // Good: There is a space after the switch keyword.
    case 0:     // Good: There is no space between the case condition and the colon.
        ...
        break;
    ...
    default:
        ...
        break;
}
```

Templates and Conversions
```cpp
// Angle brackets (< and >) are not adjacent to space. There is no space before < or between > and (.
vector<string> x;
y = static_cast<char*>(x);

// There can be a space between the type and the pointer operator. Keep the spacing style consistent.
vector<char *> x;
```

Scope Operators
```cpp
std::cout;    // Good: Namespace access. Do not leave spaces.

int MyClass::GetValue() const {}  // Good: Do not leave spaces in the definition of member functions.
```

Colons
```cpp
// Scenarios when space is required

// Good: // Add a space before or after the colon in a derived class definition.
class Sub : public Base {
    
};

// Add a space before or after the colon in the initialization list of a constructor function.
MyClass::MyClass(int var) : someVar_(var) 
{
    DoSomething();
}

// Add a space before or after the colon in a bit-field.
struct XX {
    char a : 4;     
    char b : 5;     
    char c : 4;
};
```

```cpp
// Scenarios when space is not required

// Good: // No space is added before or after the colon next to a class access permission (public or private).
class MyClass {
public:
    MyClass(int var);
private:
    int someVar_;
};

// No space is added before or after the colon in a switch statement.
switch (value)
{
    case 1:
        DoSomething();
        break;
    default:
        break;
}
```

Note: Currently, all IDEs support automatic deletion of spaces at the end of a line. Please configure your IDE correctly.

G
Gloria 已提交
715
### Rec 3.14.1 Use blank lines only when necessary to keep code compact.
N
NEEN 已提交
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749

There must be as few blank lines as possible so that more code can be displayed for easy reading. Recommendations:
- Add blank lines according to the correlation between lines.
- Consecutive blank lines are not allowed inside functions, type definitions, macros, and initialization expressions.
- A maximum of **two** consecutive blank lines can be used. 
- 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.

```cpp
int Foo()
{
    ...
}



int bar() {// Bad: More than one blank lines are used between two function definitions.
{
    ...
}


if (...) {
        // Bad: Do not add blank lines on the first and last lines of a code block.
    ...
        // Bad: Do not add blank lines on the first and last lines of a code block. 
}

int Foo(...)
{
        // Bad: Do not add blank lines before the first statement in a function body.
    ...
}
```

G
Gloria 已提交
750 751
## Classes
### 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.
N
NEEN 已提交
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
```cpp
class MyClass : public BaseClass {
public:      // Not indented.
    MyClass();  // Indented with 4 spaces.
    explicit MyClass(int var);
    ~MyClass() {}

    void SomeFunction();
    void SomeFunctionThatDoesNothing() 
    {
    }

    void SetVar(int var) { someVar_ = var; }
    int GetVar() const { return someVar_; }

private:
    bool SomeInternalFunction();

    int someVar_;
    int someOtherVar_;
};
```

In 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


G
Gloria 已提交
778
### Rule 3.15.2 The constructor initialization list must be on the same line or wrapped and aligned with four spaces of indentation.
N
NEEN 已提交
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802
```cpp
// If all variables can be placed on the same line
MyClass::MyClass(int var) : someVar_(var) 
{
    DoSomething();
}

// If the variables cannot be placed on the same line
// Wrapped at the colon and indented with four spaces
MyClass::MyClass(int var)
    : someVar_(var), someOtherVar_(var + 1)  // Good: Add a space after the comma.
{
    DoSomething();
}

//  If an initialization list needs to be placed in multiple lines, put each member on a separate line and align between lines.
MyClass::MyClass(int var)
    someVar(var),             // Four spaces of indentation.
      someOtherVar_(var + 1) 
{  
    DoSomething();
}
```

G
Gloria 已提交
803 804 805
# 4 Comments
Generally, clear architecture and good naming are recommended to improve code readability, and comments are provided only when necessary. 

N
NEEN 已提交
806 807 808 809
Comments are used to help readers quickly understand code. Therefore, **comments should be provided for the sake of readers**.

Comments must be concise, clear, and unambiguous, ensuring that information is complete and not redundant.

G
Gloria 已提交
810 811 812 813
**Comments are as important as code**. 

When 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.

N
NEEN 已提交
814 815 816 817
When 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.

Comments should be made in English.  

G
Gloria 已提交
818 819 820 821 822
## Comment Style

In C++ code, both ` /* */` and ` // ` can be used for commenting. 

Comments 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. 
N
NEEN 已提交
823 824 825 826 827

Comments of the same type must keep a consistent style.

Note: 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.

G
Gloria 已提交
828 829
## File Header Comments
### Rule 4.2.1 File header comments must contain the copyright notice.
N
NEEN 已提交
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845

/*
 * Copyright (c) 2020 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
    *
 * http://www.apache.org/licenses/LICENSE-2.0
    *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
    */

G
Gloria 已提交
846 847
## Function Header Comments
### Rule 4.3.1 Write function header comments for public functions.
W
wusongqing 已提交
848 849 850
Public 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.
Write function header comments for the function value range, return value, and precautions, since they cannot be self-explained.

G
Gloria 已提交
851
### Rule 4.3.2 Function header comments with no content are forbidden.
N
NEEN 已提交
852
Not all functions need function header comments.
G
Gloria 已提交
853

N
NEEN 已提交
854 855 856
For information that cannot be described by function signatures, add function header comments.

Function header comments are placed above the function declaration or definition. Use one of the following styles:
G
Gloria 已提交
857

N
NEEN 已提交
858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
Use '//' to start the function header.

```cpp
// Single-line function header
int Func1(void);

// Multi-line function header
// Second line
int Func2(void);
```

Use `/*   */` to start the function header.
```cpp
/* Single-line function header */
int Func1(void);

/*
 * Another single-line function header
 */
int Func2(void);

/*
 * Multi-line function header
 * Second line
 */
int Func3(void);
```
Use function names to describe functions, and add function header comments if necessary.
G
Gloria 已提交
886

N
NEEN 已提交
887 888 889
Do not write useless or redundant function headers. Do not write empty function headers with no content.

The 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.
G
Gloria 已提交
890

N
NEEN 已提交
891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
In the function interface declaration in the external header file, the function header comment should clearly describe important and useful information.

Good example:

```cpp
/*
 * The number of written bytes is returned. If -1 is returned, the write operation failed.
 * Note that the memory buffer should be released by the caller.
 */
int WriteString(const char *buf, int len);
```

Bad example:
```cpp
/*
 * Function name: WriteString
 * Function: Write a character string.
 * Parameters:
 * Return value:
 */
int WriteString(const char *buf, int len);
```
Problems:

- The 'Parameters' and 'Return value' have no content. 
- The function name is redundant.
- The most import thing, that is, who needs to release the buffer, is not clearly stated.

G
Gloria 已提交
919 920 921
## Code Comments
### Rule 4.4.1 Code comments are placed above or on the right of the corresponding code.
### 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.
N
NEEN 已提交
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946
Comments placed above code should be indented the same as that of the code.
Use one of the following styles:
Use "//".
```cpp

// Single-line comment
DoSomething();

// Multi-line comment
// Second line
DoSomething();
```

Use `/* */`.
```cpp
/* Single-line comment */
DoSomething();

/*
 * Multi-line comment in another mode
 * Second line
 */
DoSomething();
```
Leave at least one space between the code and the comment on the right. It is recommended that no more than four spaces be left.
G
Gloria 已提交
947

N
NEEN 已提交
948 949 950 951 952 953 954 955 956
You can use the Tab key to indent 1–4 spaces.

Select and use one of the following styles:

```cpp
int foo = 100;  // Comment on the right
int bar = 200;  /* Comment on the right */
```
It is more appealing sometimes when the comment is placed on the right of code and the comments and code are aligned vertically.
G
Gloria 已提交
957

N
NEEN 已提交
958
After the alignment, ensure that the comment is 1–4 spaces away from the widest line of code.
W
wusongqing 已提交
959
Example:
N
NEEN 已提交
960 961 962 963 964 965 966

```cpp
const int A_CONST = 100;         /* Related comments of the same type can be aligned vertically. */
const int ANOTHER_CONST = 200;   /* Leave spaces after code to align comments vertically. */
```
When the comment on the right exceeds the line width, consider placing the comment above the code.

G
Gloria 已提交
967
### Rule 4.4.3 Delete unused code segments. Do not comment them out.
N
NEEN 已提交
968
Code that is commented out cannot be maintained. If you attempt to restore the code, it is very likely to introduce ignorable defects.
G
Gloria 已提交
969

N
NEEN 已提交
970 971 972 973
The correct method is to delete unnecessary code directly. If necessary, consider porting or rewriting the code.

Here, 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.

G
Gloria 已提交
974 975
# 5 Header Files
## Header File Responsibility
N
NEEN 已提交
976
A header file is an external interface of a module or file. The design of a header file shows most of the system design.
G
Gloria 已提交
977

N
NEEN 已提交
978
The 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.
G
Gloria 已提交
979

N
NEEN 已提交
980 981
The header responsibility should be simple. An overly complex header file will make dependencies complex and cause long compilation times.

G
Gloria 已提交
982
### 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.
N
NEEN 已提交
983
Generally, 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.
G
Gloria 已提交
984

N
NEEN 已提交
985
If a .cpp file does not need to open any interface externally, it should not exist.
G
Gloria 已提交
986

N
NEEN 已提交
987 988
Exception: __An entry point (for example, the file where the main function is located), unit tests, and dynamic libraries __

W
wusongqing 已提交
989
Example:
N
NEEN 已提交
990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
```cpp
// Foo.h 

#ifndef FOO_H
#define FOO_H

class Foo {
public:
    Foo();
    void Fun();
    
private:
    int value_;
};

#endif
```

```cpp
// Foo.cpp
#include "Foo.h"

namespace { // 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.
    void Bar()
    {
    }
}

...

void Foo::Fun()
{
    Bar();
}
```

G
Gloria 已提交
1026 1027 1028 1029
## Header File Dependency
### Rule 5.2.1 Header file cyclic dependency is forbidden.
An 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.  

N
NEEN 已提交
1030 1031 1032 1033 1034
For 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.

The cyclic dependency of header files reflects an obviously unreasonable architecture design, which can be avoided through optimization.


G
Gloria 已提交
1035
### Rule 5.2.2 Header files should have #define guards to prevent multiple inclusion.
N
NEEN 已提交
1036 1037 1038
To prevent header files from being included multiple times, all header files should be protected by #define. Do not use #pragma once.

When defining a protection character, comply with the following rules:
G
Gloria 已提交
1039

N
NEEN 已提交
1040
1) The protection character uses a unique name.
G
Gloria 已提交
1041

N
NEEN 已提交
1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
2) Do not place code or comments (except for file header comments) before or after the protected part.

Example: 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:

```cpp
#ifndef TIMER_INCLUDE_TIMER_H
#define TIMER_INCLUDE_TIMER_H
...
#endif
```

G
Gloria 已提交
1053
### Rule 5.2.3 It is prohibited to reference external function interfaces and variables in extern declaration mode.
N
NEEN 已提交
1054
Interfaces provided by other modules or files can be used only by including header files.
G
Gloria 已提交
1055

N
NEEN 已提交
1056
Using external function interfaces and variables in extern declaration mode may cause inconsistency between declarations and definitions when external interfaces are changed.
G
Gloria 已提交
1057

N
NEEN 已提交
1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
In addition, this kind of implicit dependency may cause architecture corruption.

Cases that do not comply with specifications:

// a.cpp content
```cpp
extern int Fun();   // Bad: Use external functions in extern mode.

void Bar() 
{
    int i = Fun();
    ...
}
```

// b.cpp content
```cpp
int Fun() 
{
    // Do something
}
```
Should be changed to:

// a.cpp content
```cpp
#include "b.h"   // Good: Use the interface provided by other .cpp by including its corresponding header file.

void Bar() 
{
    int i = Fun();
    ...
}
```

// b.h content
```cpp
int Fun();
```

// b.cpp content
```cpp
int Fun() 
{
    // Do something
}
```
In some scenarios, if internal functions need to be referenced with no intrusion to the code, the extern declaration mode can be used.
G
Gloria 已提交
1106

W
wusongqing 已提交
1107
Example:
G
Gloria 已提交
1108

N
NEEN 已提交
1109
When performing unit testing on an internal function, you can use the extern declaration to reference the tested function.
G
Gloria 已提交
1110

N
NEEN 已提交
1111 1112
When a function needs to be stubbed or patched, the function can be declared using extern.

G
Gloria 已提交
1113
### Rule 5.2.4 Do not include header files in extern "C".
N
NEEN 已提交
1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155
If 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.

When 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.

For example, assume that there are two header files a.h and b.h.

// a.h content
```cpp
...
#ifdef __cplusplus
void Foo(int);
#define A(value) Foo(value)
#else
void A(int)
#endif
```
// b.h content
```cpp
...
#ifdef __cplusplus
extern "C" {
#endif

#include "a.h"
void B();

#ifdef __cplusplus
}
#endif
```

Using the C++ preprocessor to expand b.h, the following information is displayed:
```cpp
extern "C" {
    void Foo(int);
    void B();
}
```

According to the author of a.h, the function Foo is a C++ free function following the "C++" link specification.
However, because `#include "a.h"` is placed inside `extern "C"` in b.h, the link specification of function Foo is changed incorrectly.

W
wusongqing 已提交
1156
Exceptions:
N
NEEN 已提交
1157 1158
In 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"`.

G
Gloria 已提交
1159
### Rec 5.2.1 Use `#include` instead of a forward declaration to include header files.
N
NEEN 已提交
1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174
A forward declaration is for the declaration of classes, functions, and templates and is not meant for its definition.

- Advantages:
  1. Forward declarations can save compilation time. Redundant `#include `statements force the compiler to expand more files and process more input.
  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.
- Disadvantages:
  1. Forward declarations hide dependency relationship. When a header file is modified, user code will skip the necessary recompilation process.
  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.
  3. Forward declaration of symbols from the namespace `std::` is seen as undefined behavior (as specified in the C++ 11 standard specification).
  4. Forward declaration of multiple symbols from a header file can be more verbose than simply including (#include) the header.
  5. Structuring code only for forward declaration (for example, using pointer members instead of object members) can make the code more complex and slower.
  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.

Therefore, we should avoid using forward declarations as much as possible. Instead, we use the #include statement to include a header file and ensure dependency.

G
Gloria 已提交
1175
# 6 Scopes
N
NEEN 已提交
1176

G
Gloria 已提交
1177
## Namespaces
N
NEEN 已提交
1178

G
Gloria 已提交
1179
### 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.
N
NEEN 已提交
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206
In 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.

Main Reasons:
1. 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.
2. 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.
3. Use a namespace to control the scope instead of using both static and namespaces.
4. Unnamed namespaces can be used to instantiate templates rather than functions modified by the static keyword.

Do not use unnamed namespaces or static in header files.

```cpp
// Foo.cpp

namespace {
    const int MAX_COUNT = 20;
    void InternalFun(){};
}

void Foo::Fun()
{
    int i = MAX_COUNT;
    
    InternalFun();
}

```

G
Gloria 已提交
1207
### Rule 6.1.1 Do not use "using" to import namespace in a header file or before #include statements.
N
NEEN 已提交
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257
Note: Using "using" to import namespace will affect any subsequent code and may cause symbol conflicts. 
Example:

```cpp
// Header file a.h
namespace NamespaceA {
    int Fun(int);
}
```

```cpp
// Header file b.h
namespace NamespaceB {
    int Fun(int);
}

using namespace NamespaceB;

void G()
{
    Fun(1); 
}
```

```cpp
// Source code a.cpp
#include "a.h"
using namespace NamespaceA; 
#include "b.h"

void main()
{
    G(); // "using namespace NamespaceA" before #include "b.h", will cause conflicts when calling NamespaceA::Fun and NamespaceB::Fun.
}
```

Using "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.
```cpp
// foo.h

#include <fancy/string>
using fancy::string;  // Bad: It is prohibited to import symbols to the global namespace.

namespace Foo {
    using fancy::string;  // Good: Symbols can be imported in customized namespaces of modules.
    using MyVector = fancy::vector;  // Good: In C++11, aliases can be defined in customized namespaces.
}
```


G
Gloria 已提交
1258
## Global Functions and Static Member Functions
N
NEEN 已提交
1259

G
Gloria 已提交
1260
### 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.
N
NEEN 已提交
1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274
Note: 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.

If you need to define some global functions for a .cpp file, use unnamed namespaces for management.
```cpp
namespace MyNamespace {
    int Add(int a, int b);
}

class File {
public:
    static File CreateTempFile(const std::string& fileName);
};
```

G
Gloria 已提交
1275
## Global Constants and Static Member Constants
N
NEEN 已提交
1276

G
Gloria 已提交
1277
### 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.
N
NEEN 已提交
1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291
Note: 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.

If you need to define some global constants only for a .cpp file, use unnamed namespaces for management.
```cpp
namespace MyNamespace {
    const int MAX_SIZE = 100;
}

class File {
public:
    static const std::string SEPARATOR;
};
```

G
Gloria 已提交
1292
## Global Variables
N
NEEN 已提交
1293

G
Gloria 已提交
1294
### Rec 6.4.1 Do not use global variables. Use the singleton pattern instead.
N
NEEN 已提交
1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349
Note: Global variables can be modified and read, which results in data coupling between production code and the global variables.
```cpp
int g_counter = 0;

// a.cpp
g_counter++;

// b.cpp
g_counter++;

// c.cpp
cout << g_counter << endl;
```

Singleton
```cpp
class Counter {
public:
    static Counter& GetInstance()
    {
        static Counter counter;
        return counter;
    }  // Simple example of a singleton implementation
    
    void Increase() 
    {
        value_++;
    }
    
    void Print() const
    {
        std::cout << value_ << std::endl;
    }

private:
    Counter() : value_(0) {}

private:
    int value_;
};

// a.cpp
Counter::GetInstance().Increase();

// b.cpp
Counter::GetInstance().Increase();

// c.cpp
Counter::GetInstance().Print();
```

After the singleton is implemented, there is a unique global instance, which can functions as a global variable. However, the singleton provides better encapsulation.

Exception: 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.

G
Gloria 已提交
1350
# 7 Classes
N
NEEN 已提交
1351

G
Gloria 已提交
1352
## Constructors, Copy/Move Constructors, Copy/Move Assignment Operators, and Destructors
N
NEEN 已提交
1353 1354 1355 1356 1357 1358 1359 1360
Constructors, copy/move constructors, copy/move assignment operators, and destructors provide lifetime management methods for objects.
- Constructor: `X()`
- Copy constructor: `X(const X&)`
- Copy assignment operator: `operator=(const X&)`   
- Move constructor: `X (X&&)`         *Provided in versions later than C++ 11*.
- Move assignment operator: `operator=(X&&)`       *Provided in versions later than C++ 11*.
- Destructor: `~X()`

G
Gloria 已提交
1361
### Rule 7.1.1 The member variables of a class must be initialized explicitly.
N
NEEN 已提交
1362 1363
Note: 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.

W
wusongqing 已提交
1364
Exceptions:
N
NEEN 已提交
1365 1366 1367 1368 1369 1370 1371 1372
- If the member variables in a class have a default constructor, explicit initialization is not required.

Example: The following code has no constructor, and private data members cannot be initialized:
```cpp
class Message {
public:
    void ProcessOutMsg()
    {
W
wusongqing 已提交
1373
        //...
N
NEEN 已提交
1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394
    }

private:
    unsigned int msgID_;
    unsigned int msgLength_;
    unsigned char* msgBuffer_;
    std::string someIdentifier_;
};

Message message;   // The message member is not initialized.
message.ProcessOutMsg();   // Potential risks exist in subsequent use.

// Therefore, it is necessary to define a default constructor as follows:
class Message {
public:
    Message() : msgID_(0), msgLength_(0), msgBuffer_(NULL)
    {
    }

    void ProcessOutMsg()
    {
W
wusongqing 已提交
1395
        // ...
N
NEEN 已提交
1396 1397 1398 1399 1400 1401 1402 1403 1404 1405
    }

private:
    unsigned int msgID_;
    unsigned int msgLength_;
    unsigned char* msgBuffer_;
    std::string someIdentifier; // The member variable has a default constructor. Therefore, explicit initialization is not required.
};
```

G
Gloria 已提交
1406
### Rec 7.1.1 Initialization during declaration (C++ 11) and initialization using the constructor initialization list are preferred for member variables.
N
NEEN 已提交
1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423
Note: 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.

```cpp
class Message {
public:
    Message() : msgLength(0) {  // Good: The constructor initialization list is preferred.
    {
        msgBuffer = NULL;     // Bad: Values cannot be assigned in constructors.
    }
    
private:
    unsigned int msgID{0};  // Good: Used in C++11.
    unsigned int msgLength_;
    unsigned char* msgBuffer_;
};
```

G
Gloria 已提交
1424
### Rule 7.1.2 Declare single-parameter constructors as explicit to prevent implicit conversion.
N
NEEN 已提交
1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452
Note: If a single-parameter constructor is not declared as explicit, it will become an implicit conversion function.
Example:

```cpp
class Foo {
public:
    explicit Foo(const string& name): name_(name)
    {
    }
private:
    string name_;
};


void ProcessFoo(const Foo& foo){}

int main(void)
{
    std::string test = "test";
    ProcessFoo(test);  // Compiling failed.
    return 0;
}
```

The preceding code fails to be compiled because the parameter required by `ProcessFoo` is of the Foo type, which mismatch with the input string type.

If 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.

G
Gloria 已提交
1453
### Rule 7.1.3 If copy/move constructors and copy/move assignment operators are not needed, clearly prohibit them.
N
NEEN 已提交
1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466
Note: 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).
If we do not use copy constructors or copy assignment operators, explicitly delete them.

1. Set copy constructors or copy assignment operators to private and do not implement them.
```cpp
class Foo {
private:
    Foo(const Foo&);
    Foo& operator=(const Foo&);
};
```
2. Use delete provided by C++ 11. For details, see Rule 10.1.3 in chapter 10 Modern C++ Features.

W
wusongqing 已提交
1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489

3. You are advised to inherit **NoCopyable** and **NoMovable**. Do not use macros such as **DISALLOW_COPY_AND_MOVE**, **DISALLOW_COPY**, and **DISALLOW_MOVE**.
```cpp
class Foo : public NoCopyable, public NoMovable {
};
```
Implementation of NoCopyable and NoMovable:
```cpp
class NoCopyable {
public:
    NoCopyable() = default;
    NoCopyable(const NoCopyable&) = delete;
    NoCopyable& operator = (NoCopyable&) = delete;
};

class NoMovable {
public:
    NoMovable() = default;
    NoMovable(NoMovable&&) noexcept = delete;
    NoMovable& operator = (NoMovable&&) noexcept = delete;
};
```

G
Gloria 已提交
1490
### Rule 7.1.4 Copy constructors and copy assignment operators should be implemented or forbidden together.
N
NEEN 已提交
1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517
Both copy constructors and copy assignment operators provide copy semantics. They should be implemented or hidden together.

```cpp
// The copy constructor and the copy assignment operator are implemented together.
class Foo {
public:
    ...
    Foo(const Foo&);
    Foo& operator=(const Foo&);
    ...
};

// The copy constructor and the copy assignment operator are both set to default, as supported by C++ 11.
class Foo {
public:
    Foo(const Foo&) = default;
    Foo& operator=(const Foo&) = default;
};

// The copy constructor and the copy assignment operator are hidden together. You should use the delete keyword if C++11 features are available.
class Foo {
private:
    Foo(const Foo&);
    Foo& operator=(const Foo&);
};
```

G
Gloria 已提交
1518
### Rule 7.1.5 Move constructors and move assignment operators should be implemented or hidden together.
N
NEEN 已提交
1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546
The 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.

Both move constructors and move assignment operators provide move semantics. They should be implemented or hidden together.
```cpp
// The copy constructor and the copy assignment operator are implemented together.
class Foo {
public:
    ...
    Foo(Foo&&);
    Foo& operator=(Foo&&);
    ...
};

// The copy constructor and the copy assignment operator are both set to default, as supported by C++ 11.
class Foo {
public:
    Foo(Foo&&) = default;
    Foo& operator=(Foo&&) = default;
};

// The copy constructor and the copy assignment operator are hidden together. You should use the delete keyword if C++11 features are available.
class Foo {
public:
    Foo(Foo&&) = delete;
    Foo& operator=(Foo&&) = delete;
};
```

G
Gloria 已提交
1547
### Rule 7.1.6 It is prohibited to call virtual functions in constructors and destructors.
N
NEEN 已提交
1548
Note: Calling a virtual function of the current object in a constructor or destructor will cause behaviors of non-polymorphism.
G
Gloria 已提交
1549

N
NEEN 已提交
1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571
In C++, a base class constructs only one complete object at a time.

Example: Base indicates the base class, and Sub indicates the derived class.
```cpp
class Base {                       
public:                
    Base();
    virtual void Log() = 0;    // Different derived classes call different log files.
};

Base::Base()         // Base class constructor
{
    Log();           // Call the virtual function log.
}                                                  

class Sub : public Base {       
public:
    virtual void Log();          
};
```

When running the following statement:
G
Gloria 已提交
1572

N
NEEN 已提交
1573
`Sub sub;`
G
Gloria 已提交
1574

N
NEEN 已提交
1575
The 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.
G
Gloria 已提交
1576

N
NEEN 已提交
1577 1578
This also applies to destructors.

G
Gloria 已提交
1579
### 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.
W
wusongqing 已提交
1580
Slicing 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.
G
Gloria 已提交
1581

W
wusongqing 已提交
1582
[Counterexample]
G
Gloria 已提交
1583

W
wusongqing 已提交
1584
In 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.
G
Gloria 已提交
1585

W
wusongqing 已提交
1586
If 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.
G
Gloria 已提交
1587

W
wusongqing 已提交
1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611
```cpp
class Base {                       
public:                
    Base() = default;
    virtual ~Base() = default;
    ...
    virtual void Fun() { std::cout << "Base" << std::endl;}
};

class Derived : public Base {
    ...
    void Fun() override { std::cout << "Derived" << std::endl; }
};

void Foo(const Base &base)
{
    Base other = base; // Bad: Slicing occurs
    other.Fun(); // The Fun() function of the base class is called.
}
```
```cpp
Derived d;
Foo(d); // A derived class object is passed.
```
G
Gloria 已提交
1612
Set copy constructors or copy assignment operators to **private** and do not implement them.
N
NEEN 已提交
1613

G
Gloria 已提交
1614
## Inheritance
N
NEEN 已提交
1615

G
Gloria 已提交
1616
### Rule 7.2.1 Declare destructors of a base class as virtual, and declare the class that is not to be inherited as final.
N
NEEN 已提交
1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674
Note: Destructors of the derived class can be called during polymorphism invocation only when destructors of the base class are virtual.

Example: There will be memory leak if destructors of the base class are not declared as virtual.
```cpp
class Base {
public:
    virtual std::string getVersion() = 0;
    
    ~Base() 
    {
        std::cout << "~Base" << std::endl;
    }
};
```

```cpp
class Sub : public Base {
public:
    Sub() : numbers_(NULL)
    {  
    }
    
    ~Sub()
    {
        delete[] numbers_;
        std::cout << "~Sub" << std::endl;
    }
    
    int Init()
    {
        const size_t numberCount = 100;
        numbers_ = new (std::nothrow) int[numberCount];
        if (numbers_ == NULL) {
            return -1;
        }
        
        ...
    }

    std::string getVersion() 
    { 
        return std::string("hello!");
    }
private:
    int* numbers_;
};
```

```cpp
int main(int argc, char* args[])
{
    Base* b = new Sub();

    delete b;
    return 0;
}
```
Because 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.
G
Gloria 已提交
1675

W
wusongqing 已提交
1676
Exceptions:
G
Gloria 已提交
1677

W
wusongqing 已提交
1678
For 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.
N
NEEN 已提交
1679

G
Gloria 已提交
1680
### Rule 7.2.2 Do not use default parameter values for virtual functions.
N
NEEN 已提交
1681
Note: 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.
G
Gloria 已提交
1682

N
NEEN 已提交
1683
Example: 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.
G
Gloria 已提交
1684

N
NEEN 已提交
1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721
```cpp
class Base {
public:
    virtual void Display(const std::string& text = "Base!")
    {
        std::cout << text << std::endl;
    }
    
    virtual ~Base(){}
};

class Sub : public Base {
public:
    virtual void Display(const std::string& text  = "Sub!")
    {
        std::cout << text << std::endl; 
    }
    
    virtual ~Sub(){}
};

int main()
{
    Base* base = new Sub();
    Sub* sub = new Sub();
   
    ...
    
    base->Display();  // The program output is as follows: Base! The expected output is as follows: Sub!
    sub->Display();   // The program output is as follows: Sub!
    
    delete base;
    delete sub;
    return 0;
};
```

G
Gloria 已提交
1722
### Rule 7.2.3 Do not redefine inherited non-virtual functions.
N
NEEN 已提交
1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745
Note: 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.

Example:
```cpp
class Base {
public:
    void Fun();
};

class Sub : public Base { 
public:
    void Fun();
};

Sub* sub = new Sub();                     
Base* base = sub;

sub->Fun();    // Call Fun of the derived class.                  
base->Fun();   // Call Fun of the base class.
//...

```

G
Gloria 已提交
1746
## Multiple Inheritance
N
NEEN 已提交
1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757
In the actual development process, multiple inheritance is seldom used because the following typical problems may occur:
1. Data duplication and name ambiguity caused by "diamond" inheritance. C++ introduces virtual inheritance to solve these problems.
2. In addition to "diamond" inheritance, names of multiple base classes may also conflict with each other, resulting in name ambiguity.
3. 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.
4. 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.

Multiple inheritance has the following advantages:
Multiple inheritance provides a simpler method for assembling and reusing multiple interfaces or classes.

Therefore, multiple inheritance can be used only in the following cases:

G
Gloria 已提交
1758
### Rec 7.3.1 Use multi-inheritance to implement interface separation and multi-role combination.
N
NEEN 已提交
1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785
If a class requires multiple interfaces, combine multiple separated interfaces by using multiple inheritance. This is similar to the Traits mixin of the Scala language.

```cpp
class Role1 {};
class Role2 {};
class Role3 {};

class Object1 : public Role1, public Role2 {
    // ...
};

class Object2 : public Role2, public Role3 {
    // ...
};

```

The C++ standard library has a similar implementation example:
```cpp
class basic_istream {};
class basic_ostream {};

class basic_iostream : public basic_istream, public basic_ostream {
  
};
```

G
Gloria 已提交
1786
## Overloading
N
NEEN 已提交
1787 1788 1789 1790 1791 1792 1793 1794

Overload 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.
Operator overloading can make code more intuitive but has some disadvantages:
- It is often mistaken that the operation is as fast as a built-in operator, which has no performance degradation.
- There is no naming to aid debugging. It is more convenient to search by function name than by operator.
- Overloading operators can cause confusion if behavior definitions are not intuitive (for example, if the "+" operator is used for subtraction).
- 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.

G
Gloria 已提交
1795 1796 1797
# 8 Functions
## Function Design
### Rule 8.1.1 Avoid long functions and ensure that each function contains no more than 50 lines (non-null and non-comment).
N
NEEN 已提交
1798 1799 1800 1801 1802 1803 1804
A function should be displayed on one screen (no longer than 50 lines). It should do only one thing, and do it well.

Long functions often mean that the functions are too complex to implement in more than one function, or overly detailed but not further abstracted.

Exception: Some algorithms may be longer than 50 lines due to algorithm convergence and functional comprehensiveness.

Even 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.
G
Gloria 已提交
1805

N
NEEN 已提交
1806 1807
It is recommended that you split a long function into several functions that are simpler and easier to manage, facilitating comprehension and modification.

G
Gloria 已提交
1808
## Inline Functions
N
NEEN 已提交
1809

G
Gloria 已提交
1810
###  Rec 8.2.1 An inline function cannot exceed 10 lines.
N
NEEN 已提交
1811 1812 1813 1814 1815 1816 1817
**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.

Inline 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.

If 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.
**Virtual functions and recursive functions cannot be used as inline functions**.

G
Gloria 已提交
1818
## Function Parameters
N
NEEN 已提交
1819

G
Gloria 已提交
1820
### Rec 8.3.1 Use a reference instead of a pointer for function parameters.
N
NEEN 已提交
1821 1822 1823 1824 1825 1826 1827 1828

**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.

If a product is being developed for an older platform, the processing used by the old platform is preferred.
Use 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.

Exception: When the input parameter is an array with an unknown compile-time length, you can use a pointer instead of a reference.

G
Gloria 已提交
1829
### Rec 8.3.2 Use strongly typed parameters. Do not use void*.
N
NEEN 已提交
1830
While 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.
G
Gloria 已提交
1831

N
NEEN 已提交
1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864
An advantage of this is the compiler can find type mismatch problems at the compilation stage.

Using strong typing helps the compiler find more errors for us. Pay attention to the usage of the FooListAddNode function in the following code:
```cpp
struct FooNode {
    struct List link;
    int foo;
};

struct BarNode {
    struct List link;
    int bar;
}

void FooListAddNode(void *node) // Bad: Here, the void * type is used to transfer parameters.
{
    FooNode *foo = (FooNode *)node;
    ListAppend(&g_FooList, &foo->link);
}

void MakeTheList()
{
    FooNode *foo = NULL;
    BarNode *bar = NULL;
    ...

    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.
}
```

1. You can use a template function to change the parameter type.
2. A base class pointer can be used to implement this according to polymorphism.

G
Gloria 已提交
1865
### Rec 8.3.3 A function can have a maximum of five parameters.
N
NEEN 已提交
1866 1867 1868 1869 1870 1871
If 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.

If a function has more than five parameters, you can:
- Split the function.
- Combine related parameters into a struct.

G
Gloria 已提交
1872
# 9 Other C++ Features
N
NEEN 已提交
1873

G
Gloria 已提交
1874
## Constants and Initialization
N
NEEN 已提交
1875 1876 1877

Unchanged 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.

G
Gloria 已提交
1878
### Rule 9.1.1 Do not use macros to replace constants.
N
NEEN 已提交
1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891

**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.

```cpp
#define MAX_MSISDN_LEN 20    // Bad

// Use const in C++.
const int MAX_MSISDN_LEN = 20; // Good

// In versions later than C++ 11, constexpr can be used.
constexpr int MAX_MSISDN_LEN = 20;
```

G
Gloria 已提交
1892
###  Rec 9.1.1 A group of related integer constants must be defined as an enumeration.
N
NEEN 已提交
1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970

**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.

```cpp
// Good example:
enum Week {
    SUNDAY,
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY
};

enum Color {
    RED,
    BLACK,
    BLUE
};

void ColorizeCalendar(Week today, Color color);

ColorizeCalendar(BLUE, SUNDAY); // Compilation error. The parameter type is incorrect.

// Bad example:
const int SUNDAY = 0;
const int MONDAY = 1;

const int BLACK  = 0;
const int BLUE   = 1;

bool ColorizeCalendar(int today, int color);
ColorizeCalendar(BLUE, SUNDAY); // No error is reported.
```

When 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).

```cpp
// Good example: Device ID defined in the S protocol. It is used to identify a device type.
enum DeviceType {
    DEV_UNKNOWN = -1,
    DEV_DSMP = 0,
    DEV_ISMG = 1,
    DEV_WAPPORTAL = 2
};
```

Do not assign explicit values when enumeration is used internally, and only for classification.

```cpp
// Good example: Enumeration definition is used to identify session status in a program.
enum SessionState {
    INIT,
    CLOSED,
    WAITING_FOR_RESPONSE
};
```

Try to avoid repeating enumeration values. If it is required, use the already defined enumeration values instead.

```cpp
enum RTCPType {
    RTCP_SR = 200,
    RTCP_MIN_TYPE = RTCP_SR,        
    RTCP_RR    = 201,
    RTCP_SDES  = 202,
    RTCP_BYE   = 203,
    RTCP_APP   = 204,
    RTCP_RTPFB = 205,
    RTCP_PSFB  = 206,
    RTCP_XR  = 207,
    RTCP_RSI = 208,
    RTCP_PUBPORTS = 209,
    RTCP_MAX_TYPE = RTCP_PUBPORTS  
};
```

G
Gloria 已提交
1971
### Rule 9.1.2 Magic numbers cannot be used.
N
NEEN 已提交
1972 1973 1974
So-called magic numbers are numbers that are unintelligible and difficult to understand.

Some numbers can be understood based on context.
G
Gloria 已提交
1975

N
NEEN 已提交
1976
For example, the number 12 varies in different contexts.
G
Gloria 已提交
1977

N
NEEN 已提交
1978
type = 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.
G
Gloria 已提交
1979

N
NEEN 已提交
1980 1981 1982
The number 0 is often seen as a magic number. For example, `status = 0`; cannot truly express any status information.

Solution:
G
Gloria 已提交
1983

N
NEEN 已提交
1984
Comments can be added for numbers that are used locally.
G
Gloria 已提交
1985

N
NEEN 已提交
1986 1987 1988
For the numbers that are used multiple times, you must define them as constants and give them descriptive names.

The following cases are forbidden:
G
Gloria 已提交
1989

N
NEEN 已提交
1990 1991 1992
No symbol is used to explain the meaning of a number, for example, ` const int ZERO = 0`.
The symbol name limits the value. For example, for example, `const int XX_TIMER_INTERVAL_300MS = 300`. Use `XX_TIMER_INTERVAL_MS` instead.

G
Gloria 已提交
1993
### Rule 9.1.3 Ensure that a constant has only one responsibility.
N
NEEN 已提交
1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011

**Note**: A constant is used for only one specific function, that is, a constant cannot be used for multiple purposes.

```cpp
// Good example: For protocol A and protocol B, the length of the MSISDN is 20.
const unsigned int A_MAX_MSISDN_LEN = 20;
const unsigned int B_MAX_MSISDN_LEN = 20;

// Using different namespaces:
namespace Namespace1 {
    const unsigned int MAX_MSISDN_LEN = 20;
}

namespace Namespace2 {
    const unsigned int MAX_MSISDN_LEN = 20;
}
```

G
Gloria 已提交
2012
### Rule 9.1.4 Do not use memcpy_s or memset_s to initialize non-POD objects.
N
NEEN 已提交
2013 2014 2015 2016 2017 2018 2019 2020 2021

**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).

For 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.

Even 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.

For details about the POD type, see the appendix.

G
Gloria 已提交
2022
### Rec 9.1.2 Declare and initialize variables only when they are used.
N
NEEN 已提交
2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041

**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.

If 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:
* The program may become difficult to understand and maintain. The definition and use of variables are separated.
* 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.

Following 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.

```cpp
// Bad example: Declaration is separated from initialization.
string name;        // The variable is not initialized in the declaration, and a default constructor is called.
name = "zhangsan";  // An assignment operator is called again. Declaration is separate from definition, which is difficult to understand.

// Good example: Declaration and initialization are together, and easy to understand.
string name("zhangsan");  // Invoke a constructor.
```


G
Gloria 已提交
2042 2043
## Expressions
### Rule 9.2.1 A variable cannot be referenced again if it is contained in an increment or decrement operation in an expression. 
N
NEEN 已提交
2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069
In 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.
Therefore, it is recommended that an undefined operation sequence not be assumed.

Note that the problem of operation sequence cannot be solved by using parentheses because this is not a priority problem.

Example:
```cpp
x = b[i] + i++; // Bad: Whether the position of b[i] is before or after the i++ is unclear.
```
The increment or decrement operation should be placed in a single line:
```cpp
x = b[i] + i;
i++;            // Good: i++ is placed in a single line.
```

Function parameter
```cpp
Func(i++, i);   // Bad: Whether the increment operation happens for the second parameter is unclear
```

Good example:
```cpp
i++;            // Good: i++ is placed in a single line.
x = Func(i, i);
```

G
Gloria 已提交
2070
### Rule 9.2.2 A switch statement must have a default branch.
N
NEEN 已提交
2071 2072
In 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.

W
wusongqing 已提交
2073
Exceptions:
G
Gloria 已提交
2074

N
NEEN 已提交
2075
If the switch condition variables are enumerated and the case branch covers all values, the default branch is redundant.
G
Gloria 已提交
2076

N
NEEN 已提交
2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096
Because modern compilers can check which case branches are missing in the switch statement and provide an advanced warning. 

```cpp
enum Color {
    RED = 0,
    BLUE
};

// The switch condition variables are enumerated. Therefore, you do not need to add a default branch.
switch (color) {
    case RED:
        DoRedThing();
        break;
    case BLUE:
        DoBlueThing();
        ...
        break;
}
```

G
Gloria 已提交
2097
### 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.
N
NEEN 已提交
2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112
When 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.
The constant should be placed on the right. The expression is written as follows:
```cpp
if (value == MAX) {
  
}

if (value < MAX) {
  
}
```
There 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.

You 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.

G
Gloria 已提交
2113
### Rec 9.2.2 Use parentheses to specify the operator precedence.
N
NEEN 已提交
2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125
Use 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.

- For binary and ternary operators, if multiple operators are involved, parentheses should be used.
```cpp
x = a + b + c;        /* The operator does not change, and thus parentheses are not required. */
x = Foo(a + b, c);     /* The operator does not change, and thus parentheses are not required. */
x = 1 << (2 + 3);      /* More than one operator is used and thus parentheses are required. */
x = a + (b / 5);       /* More than one operator is used and thus parentheses are required. */
x = (a == b) ? a : (a  b);    /* More than one operator is used and thus parentheses are required. */
```


G
Gloria 已提交
2126
## Type Casting
N
NEEN 已提交
2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138

Do 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.

It 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:
- Whether it is unsigned or signed.
- Is it suitable for float or double?
- Should you use int8, int16, int32, or int64 bit lengths?

However, 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.

Exception: 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.

G
Gloria 已提交
2139
### Rule 9.3.1 If type casting is required, use the type casting provided by the C++ instead of the C style.
N
NEEN 已提交
2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156

**Note**:

The 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:
- Type casting:
1. `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.
2. `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.
3. `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.
4. `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.

- Arithmetic conversion: (Supported by C++ 11 and later versions)
  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.
```cpp
  double d{ someFloat };
  int64_t i{ someInt32 };
```

G
Gloria 已提交
2157
### Rec 9.3.1 Avoid using `dynamic_cast`.
N
NEEN 已提交
2158 2159 2160
1. `dynamic_cast` depends on the RTTI of C++ so that the programmer can identify the type of the object in C++ at run time.
2. `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.

G
Gloria 已提交
2161
### Rec 9.3.2 Avoid using `reinterpret_cast`.
N
NEEN 已提交
2162 2163 2164

**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.

G
Gloria 已提交
2165
### Rec 9.3.3 Avoid using `const_cast`.
N
NEEN 已提交
2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202

**Note**: The `const_cast` command is used to remove the `const`  and `volatile` properties of an object.

The action of using a pointer or reference after the const_cast conversion to modify the const property of an object is undefined.

```cpp
// Bad example:
const int i = 1024; 
int* p = const_cast<int*>(&i);
*p = 2048;      // The action is undefined.
```

```cpp
// Bad example:
class Foo {
public:
    Foo() : i(3) {}

    void Fun(int v) 
    {
        i = v;
    }

private:
    int i;
};

int main(void)
{
    const Foo f;
    Foo* p = const_cast<Foo*>(&f);
    p->Fun(8);  // The action is undefined.
}

```


G
Gloria 已提交
2203
## Resource Allocation and Release
N
NEEN 已提交
2204

G
Gloria 已提交
2205
### Rule 9.4.1 When a single object is released, delete is used. When an array object is released, delete [] is used.
N
NEEN 已提交
2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232
Note: To delete a single object, use delete; to delete an array object, use delete []. The reasons are as follows:

- new: Apply for memory from the system and call the corresponding constructor to initialize an object.
- new[n]: Apply for memory for n objects and call the constructor n times for each object to initialize them.
- delete: Call the corresponding destructor first and release the memory of an object.
- delete[]: Call the corresponding destructor for each object and release their memory.

If 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.

Bad example:
```cpp
const int MAX_ARRAY_SIZE = 100;
int* numberArray = new int[MAX_ARRAY_SIZE];
...
delete numberArray;
numberArray = NULL;
```

Good example:
```cpp
const int MAX_ARRAY_SIZE = 100;
int* numberArray = new int[MAX_ARRAY_SIZE];
...
delete[] numberArray;
numberArray = NULL;
```

G
Gloria 已提交
2233
### Rec 9.4.1 Use the RAII feature to trace dynamic allocation.
N
NEEN 已提交
2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248

Note: 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.

The 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:
- We do not need to explicitly release resources.
- 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.


In the following example, RAII removes the need for explicit release of mutex resources.

```cpp
class LockGuard {
public:
    LockGuard(const LockType& lockType): lock_(lockType)
    {
L
liuhui 已提交
2249
        lock_.Acquire();
N
NEEN 已提交
2250 2251 2252 2253
    }
    
    ~LockGuard()
    {
L
liuhui 已提交
2254
        lock_.Release();
N
NEEN 已提交
2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274
    }
    
private:
    LockType lock_;
};


bool Update()
{
    LockGuard lockGuard(mutex);
    if (...) {
        return false;
    } else {
        // Data operations
    }
    
    return true;
}
```

G
Gloria 已提交
2275
## Standard Template Library
N
NEEN 已提交
2276 2277 2278

The standard template library (STL) varies between products. The following table lists some basic rules and suggestions for each team.

G
Gloria 已提交
2279
### Rule 9.5.1 Do not save the pointer returned by c_str () of std::string.
N
NEEN 已提交
2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310

Note: 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.

Example:

```cpp
void Fun1()
{
    std::string name = "demo";
    const char* text = name.c_str(); // After the expression ends, the life cycle of name is still in use and the pointer is valid.

    // If a non-const member function (such as operator[] and begin()) of the string type is invoked and the string is modified,
    // The text may become unavailable or may not be the original string.
    name = "test";
    name[1] = '2';

    // When the text pointer is used next time, the string is no longer "demo".
}

void Fun2()
{
    std::string name = "demo";
    std::string test = "test";
    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.

    // When the text pointer is used next time, it no longer points to the valid memory space.
}
```
Exception: 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.


G
Gloria 已提交
2311
### Rec 9.5.1 Use std::string instead of char*.
N
NEEN 已提交
2312 2313

Note: Using string instead of `char*` has the following advantages:
W
wusongqing 已提交
2314
1. There is no need to consider the null character '\0' at the end.
N
NEEN 已提交
2315
2. You can directly use operators such as `+`,  `=`, and `==`, and other character and string operation functions.
G
Gloria 已提交
2316
3. There is no need to consider memory allocation operations. This helps avoid explicit usage of `new` and `delete` and the resulting errors.
N
NEEN 已提交
2317 2318 2319 2320

Note 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.

Exception:
G
Gloria 已提交
2321

N
NEEN 已提交
2322
When 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.
G
Gloria 已提交
2323

N
NEEN 已提交
2324 2325
When 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>`.

G
Gloria 已提交
2326
### Rule 9.5.2 Do not use auto_ptr.
N
NEEN 已提交
2327 2328 2329 2330 2331 2332
Note: The `std::auto_ptr` in the STL library has an implicit ownership transfer behavior. The code is as follows:
```cpp
auto_ptr<T> p1(new T);
auto_ptr<T> p2 = p1;
```
After 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.
G
Gloria 已提交
2333

N
NEEN 已提交
2334
This 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.
G
Gloria 已提交
2335

N
NEEN 已提交
2336
There 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.
G
Gloria 已提交
2337

N
NEEN 已提交
2338
In the first scenario, you can use std::shared_ptr instead.
G
Gloria 已提交
2339

N
NEEN 已提交
2340 2341 2342
In 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.

Exception:
G
Gloria 已提交
2343

N
NEEN 已提交
2344 2345 2346
Before 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.


G
Gloria 已提交
2347
### Rec 9.5.2 Use the new standard header files.
N
NEEN 已提交
2348 2349 2350 2351

Note:
When using the standard header file of C++, use `<cstdlib>` instead of `<stdlib.h>`.

G
Gloria 已提交
2352
## Usage of const
N
NEEN 已提交
2353
Add 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.
G
Gloria 已提交
2354

N
NEEN 已提交
2355 2356
Sometimes it is better to use constexpr from C++ 11 to define real constants.

G
Gloria 已提交
2357
### Rule 9.6.1 For formal parameters of pointer and reference types, if the parameters do not need to be modified, use const.
N
NEEN 已提交
2358 2359 2360 2361 2362 2363 2364
Unchanging 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.
```cpp
class Foo;

void PrintFoo(const Foo& foo);
```

G
Gloria 已提交
2365
### Rule 9.6.2 For member functions that do not modify member variables, use const.
N
NEEN 已提交
2366
Declare 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.
G
Gloria 已提交
2367

N
NEEN 已提交
2368
When 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.
G
Gloria 已提交
2369

N
NEEN 已提交
2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390
```cpp
class Foo {
public:

    // ...

    int PrintValue() const // const modifies member functions and does not modify member variables.
    {
        std::cout << value_ << std::endl;
    }

    int GetValue() const  // const modifies member functions and does not modify member variables.
    {
        return value_;
    }

private:
    int value_;
};
```

G
Gloria 已提交
2391
### Rec 9.6.1 Member variables that will not be modified after initialization should be defined as constants.
N
NEEN 已提交
2392 2393 2394 2395 2396 2397 2398 2399 2400 2401

```cpp
class Foo {
public:
    Foo(int length) : dataLength_(length) {}
private:
    const int dataLength_;  
};
```

G
Gloria 已提交
2402
## Exceptions
N
NEEN 已提交
2403

G
Gloria 已提交
2404
### Rec 9.7.1 If the function does not throw an exception, the declaration is `noexcept`.
N
NEEN 已提交
2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421
**Reasons:**
1. 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.
2. 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:

```cpp
extern "C" double sqrt(double) noexcept;  // No exceptions are thrown.

// noexcept can still be used when exceptions may be thrown.
// The exception of memory exhaustion is not processed. The function is simply declared as noexcept.
std::vector<int> MyComputation(const std::vector<int>& v) noexcept
{
    std::vector res = v;    // Exceptions may be thrown.
    // do something
    return res;
}
```

G
Gloria 已提交
2422
**Example**
N
NEEN 已提交
2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448

```cpp
RetType Function(Type params) noexcept;   // Maximized optimization
RetType Function(Type params) noexcept;   // No optimization

// Declaration as noexcept for the move operation of std::vector is needed.
class Foo1 {
public:
    Foo1(Foo1&& other);  // no noexcept
};

std::vector<Foo1> a1;
a1.push_back(Foo1());
a1.push_back(Foo1()); // The copy constructor is called to enable the container expansion and removal of existing items.

class Foo2 {
public:
    Foo2(Foo2&& other) noexcept;
};

std::vector<Foo2> a2;
a2.push_back(Foo2());
a2.push_back(Foo2()); //Triggers container expansion and invokes the move constructor to move existing elements.
```

**Note**
G
Gloria 已提交
2449

N
NEEN 已提交
2450 2451
The default constructor, destructor, `swap` function, and `move` operator should not throw an exception.

G
Gloria 已提交
2452
## Templates and Generic Programming
N
NEEN 已提交
2453

G
Gloria 已提交
2454
### Rule 9.8.1 Do not use generic programming.
W
wusongqing 已提交
2455
OpenHarmony adopts object-oriented programming, which has ideas, concepts, and techniques totally different from those of generic programming.
N
NEEN 已提交
2456

W
wusongqing 已提交
2457
Generic 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.
N
NEEN 已提交
2458

W
wusongqing 已提交
2459
However, generic programming has the following disadvantages:
N
NEEN 已提交
2460

W
wusongqing 已提交
2461 2462 2463 2464 2465
1. 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.
2. The techniques used in generic programming are often obscure to anyone but language experts. Template code can be unreadable in certain cases.
3. 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.
4. Inappropriate use of templates cause code bloat during runtime.
5. 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.
N
NEEN 已提交
2466

W
wusongqing 已提交
2467
Only __a few components of OpenHarmony__ support generic programming, and the templates developed using generic programming must have detailed comments.
G
Gloria 已提交
2468

W
wusongqing 已提交
2469
Exceptions:
N
NEEN 已提交
2470

G
Gloria 已提交
2471 2472 2473
The STL adaptation layer can use generic programming.

## Macros
N
NEEN 已提交
2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488
In the C++ language, it is strongly recommended that complex macros be used as little as possible.
- For constant definitions, use `const` or `enum` as stated in the preceding sections. 
- For macro functions, try to be as simple as possible, comply with the following principles, and use inline functions and template functions for replacement.

```cpp
// The macro function is not recommended.
#define SQUARE(a, b) ((a) * (b)) 

// Use the template function and inline function as a replacement.
template<typename T> T Square(T a, T b) { return a * b; }
```

For details about how to use macros, see the related chapters about the C language specifications.
**Exception**: For some common and mature applications, for example, encapsulation for new and delete, the use of macros can be retained.

G
Gloria 已提交
2489
# 10 Modern C++ Features
N
NEEN 已提交
2490 2491 2492 2493

As 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.
This chapter describes some guidelines for modern C++ use, to avoid language pitfalls.

G
Gloria 已提交
2494 2495
## Code Simplicity and Security Improvement
### Rec 10.1.1 Use `auto` properly.
N
NEEN 已提交
2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546
**Reasons**

* `auto` can help you avoid writing verbose, repeated type names, and can also ensure initialization when variables are defined.
* The `auto` type deduction rules are complex and need to be read carefully.
* If using `auto` makes the code clearer, use a specific type of it and use it only for local variables.

**Example**

```cpp
// Avoid verbose type names.
std::map<string, int>::iterator iter = m.find(val);
auto iter = m.find(val);

// Avoid duplicate type names.
class Foo {...};
Foo* p = new Foo;
auto p = new Foo;

// Ensure that the initialization is successful.
int x;    // The compilation is correct but the variable is not initialized.
auto x;   // The compilation failed. Initialization is needed.
```

`auto` type deduction may cause the following problems:

```cpp
auto a = 3;           // int
const auto ca = a;    // const int
const auto& ra = a;   // const int&
auto aa = ca;         // int, const and reference are neglected.
auto ila1 = { 10 };   // std::initializer_list<int>
auto ila2{ 10 };      // std::initializer_list<int>

auto&& ura1 = x;      // int&
auto&& ura2 = ca;     // const int&
auto&& ura3 = 10;     // int&&

const int b[10];
auto arr1 = b;        // const int*
auto& arr2 = b;       // const int(&)[10]
```

If you do not pay attention to `auto` type deduction and ignore the reference, hard-to-find performance problems may be created.

```cpp
std::vector<std::string> v;
auto s1 = v[0]; // auto deduction changes s1 to std::string in order to copy v[0].
```

If `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.

G
Gloria 已提交
2547 2548 2549
### Rule 10.1.1 Use the keyword `override` when rewriting virtual functions.
**Reason**

N
NEEN 已提交
2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577
The 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.

If 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.

**Example**

```cpp
class Base {
public:
    virtual void Foo();
    virtual void Foo(int var);
    void Bar();
};

class Derived : public Base {
public:
    void Foo() const override; // Compilation failed: derived::Foo is different from that of the prototype of base::Foo and is not overridden.
    void Foo() override;       // Compilation successful: derived::Foo overrode base::Foo.
    void Foo(int var) final;   // Compilation successful: Derived::Foo(int) rewrites Base::Foo(int), and the derived class of Derived cannot override this function.
    void Bar() override;       // Compilation failed: base::Bar is not a virtual function.
};
```

**Summary**
1. When defining the virtual function for the first time based on the base class, use the keyword `virtual`.
2. When overriding the virtual function by a subclass in a base class, including destructors, use the keyword `override` or  `final` instead of `virtual`.
3. For the non-virtual function, do not use `virtual` or `override`.

G
Gloria 已提交
2578
### Rule: 10.1.2 Use the keyword `delete` to delete functions.
N
NEEN 已提交
2579
**Reason**
G
Gloria 已提交
2580

N
NEEN 已提交
2581 2582
The `delete` keyword is clearer and the application scope is wider than a class member function that is declared as private and not implemented.

G
Gloria 已提交
2583
**Example**
N
NEEN 已提交
2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608

```cpp
class Foo {
private:
    // Whether the copy structure is deleted or not is unknown because usually only the header file is checked.
    Foo(const Foo&);
};

class Foo {
public:
    // Explicitly delete the copy assignment operator.
    Foo& operator=(const Foo&) = delete;
};
```

The `delete` keyword can also be used to delete non-member functions.

```cpp
template<typename T>
void Process(T value);

template<>
void Process<void>(void) = delete;
```

G
Gloria 已提交
2609 2610 2611
### Rule 10.1.3 Use `nullptr` instead of `NULL` or `0`.
**Reason**

N
NEEN 已提交
2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663
For a long time, C++ has not had a keyword that represents a null pointer, which is embarrassing:

```cpp
#define NULL ((void *)0)

char* str = NULL;   // Error: void* cannot be automatically converted to char*.

void(C::*pmf)() = &C::Func;
if (pmf == NULL) {} // Error: void* cannot be automatically converted to the pointer that points to the member function.
```

If `NULL` is defined as `0` or `0L`, the above problems can be solved.

Alternatively, 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.

```cpp
auto result = Find(id);
if (result == 0) {  // Does Find() return a pointer or an integer?
    // do something
}
```

Literally `0` is of the `int` type (`0L` is the `long` type). Therefore, neither `NULL` nor `0` is a pointer type.
When a function of the pointer or integer type is overloaded, `NULL` or `0` calls only the overloaded pointer function.

```cpp
void F(int);
void F(int*);

F(0);      // Call F(int) instead of F(int*).
F(NULL);   // Call F(int) instead of F(int*).
```

In addition, `sizeof(NULL) == sizeof(void*)` does not always make sense, which is a potential risk.

Summary: 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.

`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.

`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.

```cpp
void F(int);
void F(int*);
F(nullptr);   // Call F(int*).

auto result = Find(id);
if (result == nullptr) {  // Find() returns a pointer.
    // do something
}
```

G
Gloria 已提交
2664
### Rule 10.1.4 Use `using` instead of `typedef`.
N
NEEN 已提交
2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718
For 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>>`.

```cpp
typedef std::map<uint32_t, std::vector<int>> SomeType;
```

Using 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.
For versions supporting C++ 11 features, `using` is provided to implement `alias declarations`:

```cpp
using SomeType = std::map<uint32_t, std::vector<int>>;
```

Compare the two formats:

```cpp
typedef Type Alias;   // It cannot be told whether the original Type or Alias is at the front.
using Alias = Type;   // The format confirms to the assignment rule. It is easy to understand and helps reduce errors.
```

If this is not enough to prove the advantages of `using`, the alias template may be a better example:

```cpp
//: Only one line of code is need to define an alias for a template.
template<class T>
using MyAllocatorVector = std::vector<T, MyAllocator<T>>;

MyAllocatorVector data;       // An alias for a template defined with "using".

template<class T>
class MyClass {
private:
    MyAllocatorVector data_;   // Another.
};
```

`typedef` does not support alias templates and they have to be hacked in.

```cpp
// A template is used for packaging typedef. Therefore, a template class is needed.
template<class T>
struct MyAllocatorVector {
    typedef std::vector<T, MyAllocator<T>> type;
};

MyAllocatorVector::type data;  // ::type needs to be added when using typedef to define an alias.

template<class T>
class MyClass {
private:
    typename MyAllocatorVector::type data_;  // For a template class, typename is also needed in addition to ::type.
};
```

G
Gloria 已提交
2719
### Rule 10.1.5 Do not use std::move to operate the const object.
N
NEEN 已提交
2720
Literally, `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.
G
Gloria 已提交
2721

N
NEEN 已提交
2722 2723
Regarding 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.

G
Gloria 已提交
2724 2725
**Bad example**

N
NEEN 已提交
2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738
```cpp
std::string g_string;
std::vector<std::string> g_stringList;

void func()
{
    const std::string myString = "String content";
    g_string = std::move(myString); // Bad: myString is not moved. Instead, it is copied.
    const std::string anotherString = "Another string content";
    g_stringList.push_back(std::move(anotherString));    // Bad: anotherString is not moved. Instead, it is copied.
}
```

G
Gloria 已提交
2739 2740 2741
## Smart Pointers
### 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.
**Reason**
W
wusongqing 已提交
2742 2743 2744
Smart 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.

When 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.
N
NEEN 已提交
2745

G
Gloria 已提交
2746
**Example**
N
NEEN 已提交
2747 2748

```cpp
W
wusongqing 已提交
2749 2750 2751 2752 2753 2754 2755
class Foo;
class Base {
public:
    Base() {}
    virtual ~Base() 
    {
        delete foo_;
N
NEEN 已提交
2756
    }
W
wusongqing 已提交
2757 2758 2759
private:
    Foo* foo_ = nullptr;
};
N
NEEN 已提交
2760 2761
```

W
wusongqing 已提交
2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786
**Exceptions**
1. When a created object is returned, a smart pointer can be used if the pointer destructor is required.
```cpp
class User;
class Foo {
public:
    std::unique_ptr<User, void(User *)> CreateUniqueUser() // Use unique_ptr to ensure that the object is created and released in the same runtime.
    {
        sptr<User> ipcUser = iface_cast<User>(remoter);
        return std::unique_ptr<User, void(User *)>(::new User(ipcUser), [](User *user) {
            user->Close();
            ::delete user;
        });
    }

    std::shared_ptr<User> CreateSharedUser() // Use shared_ptr to ensure that the object is created and released in the same runtime.
    {
        sptr<User> ipcUser = iface_cast<User>(remoter);
        return std::shared_ptr<User>(ipcUser.GetRefPtr(), [ipcUser](User *user) mutable {
            ipcUser = nullptr;
        });
    }
};
```
2. When the created object is returned and needs to be referenced by multiple parties, `shared_ptr` can be used.
N
NEEN 已提交
2787

G
Gloria 已提交
2788 2789 2790
### Rule 10.2.2 Use `unique_ptr` instead of `shared_ptr`.
**Reasons**

N
NEEN 已提交
2791 2792 2793 2794
1. Using `shared_ptr` a lot has an overhead (atomic operations on the `shared_ptr`s reference count have a measurable cost).
2. Shared ownership in some cases (such as circular dependency) may create objects that can never be released.
3. Shared ownership can be an attractive alternative to careful ownership design but it may obfuscate the design of a system.

G
Gloria 已提交
2795 2796 2797
### Rule 10.2.2 Use `std::make_unique` instead of `new` to create a `unique_ptr`.
**Reasons**

N
NEEN 已提交
2798 2799 2800
1. `make_unique` provides a simpler creation method.
2. `make_unique` ensures the exception safety of complex expressions.

G
Gloria 已提交
2801
**Example**
N
NEEN 已提交
2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827

```cpp
// Bad: MyClass appears twice, which carries a risk of inconsistency.
std::unique_ptr<MyClass> ptr(new MyClass(0, 1));
// Good: MyClass appears once and there is no possibility of inconsistency.
auto ptr = std::make_unique<MyClass>(0, 1);
```

Recurrence of types may cause serious problems, and it is difficult to find them:

```cpp
// The code compiles fine, but new and delete usage does not match.
std::unique_ptr<uint8_t> ptr(new uint8_t[10]);
std::unique_ptr<uint8_t[]> ptr(new uint8_t);
// No exception safety: The compiler may calculate parameters in the following order:
// 1. Allocate the memory of Foo.
// 2. Construct Foo.
// 3. Call Bar.
// 4. Construct unique_ptr<Foo>.
// If Bar throws an exception, Foo is not destroyed and a memory leak occurs.
F(unique_ptr<Foo>(new Foo()), Bar());

// Exception safety: Calling of function is not interrupted.
F(make_unique<Foo>(), Bar());
```

G
Gloria 已提交
2828 2829
**Exception**

N
NEEN 已提交
2830
`std::make_unique` does not support user-defined `deleter`.
G
Gloria 已提交
2831

W
wusongqing 已提交
2832
In the scenario where the `deleter` needs to be customized, it is recommended that `make_unique` be implemented in the customized version's own namespace.
G
Gloria 已提交
2833

N
NEEN 已提交
2834 2835
Using `new` to create `unique_ptr` with the user-defined `deleter` is the last choice.

G
Gloria 已提交
2836 2837 2838
### Rule 10.2.4 Create `shared_ptr` by using `std::make_shared` instead of `new`.
**Reason**

N
NEEN 已提交
2839 2840
In addition to the consistency factor similar to that in `std::make_unique` when using `std::make_shared`, performance is also a factor to consider.
`std::shared_ptr` manages two entities:
G
Gloria 已提交
2841

N
NEEN 已提交
2842 2843 2844 2845 2846
* Control block (storing reference count, `deleter`, etc.)
* Managed objects

When `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.

G
Gloria 已提交
2847 2848
**Exception**

N
NEEN 已提交
2849 2850
Similar to `std::make_unique`, `std::make_shared` does not support `deleter` customization.

G
Gloria 已提交
2851 2852 2853 2854
## Lambda
### Rec 10.3.1 Use `lambda` to capture local variables or write local functions when normal functions do not work.
**Reason**

N
NEEN 已提交
2855
Functions cannot capture local variables or be declared at local scope. If you need those things, choose `lambda` instead of handwritten `functor`.
G
Gloria 已提交
2856

N
NEEN 已提交
2857
On the other hand, `lambda` and `functor` objects do not support overloading. If overloading is required, use a function.
G
Gloria 已提交
2858

N
NEEN 已提交
2859 2860
If both `lambda` and functions work, a function is preferred. Use the simplest tool.

G
Gloria 已提交
2861
**Example**
N
NEEN 已提交
2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877

```cpp
// Write a function that accepts only an int or string.
// -- Overloading is more natural.
void F(int);
void F(const string&);

// The local state needs to be captured or appear in the statement or expression range.
// -- A lambda is more natural.
vector<Work> v = LotsOfWork();
for (int taskNum = 0; taskNum < max; ++taskNum) {
    pool.Run([=, &v] {...});
}
pool.Join();
```

G
Gloria 已提交
2878 2879 2880
### Rule 10.3.1 Avoid capturing by reference in lambdas that will not be used locally.
**Reason**

N
NEEN 已提交
2881 2882
Using `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.

G
Gloria 已提交
2883
**Example**
N
NEEN 已提交
2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905

```cpp
// Bad
void Foo()
{
    int local = 42;
    // Capture a reference to a local variable.
    // After the function returns results, local no longer exists,
    // Process() call will have undefined behavior.
    threadPool.QueueWork([&]{ Process(local); });
}

// Good
void Foo()
{
    int local = 42;
    // Capture a copy of local.
    // Since a copy of local is made, it will be always available for the call.
    threadPool.QueueWork([=]{ Process(local); });
}
```

G
Gloria 已提交
2906 2907 2908
### Rec 10.3.2 All variables are explicitly captured if `this` is captured.
**Reason**

N
NEEN 已提交
2909 2910
The `[=]` 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.

G
Gloria 已提交
2911
**Example**
N
NEEN 已提交
2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934

```cpp
class MyClass {
public:
    void Foo()
    {
        int i = 0;

        auto Lambda = [=]() { Use(i, data_); };   // Bad: It looks like we are copying or capturing by value but member variables are actually captured by reference.

        data_ = 42;
        Lambda(); // Call use(42);
        data_ = 43;
        Lambda(); // Call use(43);

        auto Lambda2 = [i, this]() { Use(i, data_); }; // Good: the most explicit and least confusing method.
    }

private:
    int data_ = 0;
};
```

G
Gloria 已提交
2935 2936 2937
### Rec 10.3.3 Avoid default capture modes.
**Reason**

N
NEEN 已提交
2938
The lambda expression provides two default capture modes: by-reference (&) and by-value (=).
G
Gloria 已提交
2939

N
NEEN 已提交
2940
By 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.
G
Gloria 已提交
2941 2942 2943

By 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.

N
NEEN 已提交
2944 2945
Therefore, it is required to clearly state the variables that lambda needs to capture, instead of using the default capture mode.

G
Gloria 已提交
2946 2947
**Bad example**

N
NEEN 已提交
2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960
```cpp
auto func()
{
    int addend = 5;
    static int baseValue = 3;

    return [=]() {  // Only addend is actually copied.
        ++baseValue;    // The modification will affect the value of the static variable.
        return baseValue + addend;
    };
}
```

G
Gloria 已提交
2961 2962
**Good example**

N
NEEN 已提交
2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977
```cpp
auto func()
{
    int addend = 5;
    static int baseValue = 3;

    return [addend, baseValue = baseValue]() mutable {  // Uses the C++14 capture initialization to copy a variable.
        ++baseValue;    // Modifying the copy of a static variable does not affect the value of the static variable.
        return baseValue + addend;
    };
}
```

Reference: Effective Modern C++: Item 31: Avoid default capture modes.

G
Gloria 已提交
2978 2979 2980 2981
## Interfaces
### Rec 10.4.1 Use `T*` or `T&` arguments instead of a smart pointer in scenarios where ownership is not involved.
**Reasons**

N
NEEN 已提交
2982
1. Passing a smart pointer to transfer or share ownership should only be used when the ownership mechanism is explicitly required.
G
Gloria 已提交
2983
2. Passing a smart pointer (for example, passing `this` ) restricts the use of a function to callers using smart pointers.
N
NEEN 已提交
2984 2985
3. Passing a shared smart pointer adds a runtime performance cost.

G
Gloria 已提交
2986
**Example**
N
NEEN 已提交
2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011

```cpp
// Accept any int*.
void F(int*);

// Accept only integers for which you want to transfer ownership.
void G(unique_ptr<int>);

// Accept only integers for which you want to share ownership.
void G(shared_ptr<int>);

// Does not need to change the ownership but requires ownership of the caller.
void H(const unique_ptr<int>&);

// Accept any int.
void H(int&);

// Bad
void F(shared_ptr<Widget>& w)
{
    // ...
    Use(*w); // When only w is used, lifecycle management is not required.
    // ...
};
```