Rules are not perfect. Disabling useful features in specific situations may affect code implementation. However, the rules are formulated "to help most programmers to get more benefits". If a rule is found unhelpful or difficult to follow in team coding, please send your feedback to us so we can improve the rule accordingly. Before referring to this guide, you are expected to have the following basic capabilities for C++. It is not for a beginner that wants to learn about C++.
Rules are not perfect. Disabling useful features in specific situations
may affect code implementation. However, the rules are formulated “to
help most programmers to get more benefits”. If a rule is found
unhelpful or difficult to follow in team coding, please send your
feedback to us so we can improve the rule accordingly. Before referring
to this guide, you are expected to have the following basic capabilities
for C++. It is not for a beginner that wants to learn about C++. 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. 3. Have a
general knowledge of the C++ Standard Library.
#. Have a general knowledge of ISO standards for C++.
#. Be familiar with the basic features of C++, including those of C++ 03/11/14/17.
#. Have a general knowledge of the C++ Standard Library.
General Principles
------------------
******************
Code must meet the requirements for readability, maintainability, security, reliability, testability, efficiency, and portability while ensuring functionality correctness.
Code must meet the requirements for readability, maintainability,
security, reliability, testability, efficiency, and portability while
ensuring functionality correctness.
Conventions
***********
Key Points
----------
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.
Conventions
-----------
**Rule**: a regulating principle that must be followed during
**Rule**: A regulating principle that must be followed during
programming.
**Recommendation**: a guideline that must be considered during
**Recommendation**: A guideline that must be considered during
programming.
This document is applicable to standard C++ versions (C++ 03/11/14/17)
unless otherwise specified in the rule.
Exceptions
----------
Exceptions
**********
It is necessary to understand the reason for each rule or recommendation
and to try and comply with them. However, some rules and recommendations
...
...
@@ -57,15 +37,14 @@ principles and provide appropriate reasons for the exception. Try to
avoid exceptions because they affect the code consistency. Exceptions to
‘Rules’ should be very rare.
| The style consistency principle is preferred in the following case:
| When you modify external open source or third-party code, the existing
code specifications prevail.
The style consistency principle is preferred in the following case:
When you modify external open source or third-party code, the existing code specifications prevail.
2 Naming
========
Naming
******
General Naming Rules
--------------------
====================
**CamelCase** CamelCase is the practice of writing compound words or
phrases so that each word or abbreviation in the phrase begins with a
...
...
@@ -102,11 +81,11 @@ scope, the namespace scope, and the scope of a static member of a class.
**Variable** indicates the variables excluding those defined in
**Constant**. These variables use the lowerCamelCase style.
File Names
----------
File Names
==========
Recommendation 2.2.1 Use .cpp as the C++ file name extension and .h as the header file name extension.
Global variables should be used as little as possible, and special
attention should be paid to the use of global variables. This prefix
...
...
@@ -250,7 +228,7 @@ common member variables of classes are named in the same way.
}
Rule 2.5.2 Name member variables in classes based on the three styles of the lowerCamelCase and maintain a uniform coding style for a product or project.
Only spaces can be used for indentation. Four spaces are indented each
time. Do not use the Tab character to indent. Currently, almost all IDEs
...
...
@@ -335,10 +313,10 @@ support automatic expansion of a Tab to 4 spaces upon pressing the tab
key. Please configure your IDE to do so.
Braces
------
======
Rule 3.3.1 Use the K&R indentation writing style.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-------------------------------------------------
**K&R style** When a line break is required, the left brace of a
function (excluding the lambda statement) starts a new line. One space
...
...
@@ -389,11 +367,11 @@ same line.
int value_;
};
Function Declarations and Definitions
-------------------------------------
Function Declarations and Definitions
=====================================
Rule 3.4.1 The return type and the function name of a function declaration or definition must be on the same line. When the length of the function parameter list exceeds the permitted length, a line break is required and parameters must be aligned appropriately.
When a function is declared or defined, the return value type of the
function should be on the same line as the function name. If the line
...
...
@@ -432,10 +410,10 @@ The following is an example of line breaks:
}
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.
Reference naming: There can be only one space around ``&``.
...
...
@@ -711,11 +689,11 @@ Reference naming: There can be only one space around ``&``.
int & p = i; // Bad
int&p = i; // Bad
Preprocessor Directives
-----------------------
Preprocessor Directives
=======================
Rule 3.13.1 The number sign (#) that starts a preprocessor directive must be at the beginning of the line and can be indented in nested preprocessor directives.
There must be as few blank lines as possible so that more code can be
displayed for easy reading. Recommendations: - Add blank lines according
...
...
@@ -960,11 +938,11 @@ a namespace.
...
}
Classes
-------
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.
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.
TODO/TBD comments are used to describe required improvements and
supplements. FIXME comments are used to describe defects that need
fixing. They should have a standardized style, which facilitates text
search. For example:
.. code:: cpp
// TODO(<author-name>): XX
// FIXME: XX
5 Header Files
==============
Header File Responsibility
--------------------------
A header file is an external interface of a module or file. The design
of a header file shows most of the system design. 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. The header responsibility should be simple. An overly
complex header file will make dependencies complex and cause long
compilation times.
Recommendation 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.
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. If a .cpp file does not need to open
any interface externally, it should not exist. Exception: **An entry
point (for example, the file where the main function is located), unit
tests, and dynamic libraries**
For example:
.. code:: cpp
// Foo.h
#ifndef FOO_H
#define FOO_H
class Foo {
public:
Foo();
void Fun();
private:
int value_;
};
#endif
.. code:: 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();
}
Header File Dependency
----------------------
Rule 5.2.1 Header file cyclic dependency is forbidden.
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.
6 Scopes
========
Namespaces
----------
Recommendation 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.
std::string someIdentifier; // The member variable has a default constructor. Therefore, explicit initialization is not required.
};
Recommendation 7.1.1 Initialization during declaration (C++ 11) and initialization using the constructor initialization list are preferred for member variables.
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. 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:
.. 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.
Recommendation 8.3.3 A function can have a maximum of five parameters.
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:
.. code:: 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.
Recommendation 9.5.1 Use std::string instead of char*.
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``.
Rule: 10.1.2 Use the keyword ``delete`` to delete functions.
**Reason:** 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.
**Example:**
.. code:: 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.