1 basic knowledge
1.1 C + + input and output
#include<iostream> using namespace std; int main() { cout<<"Hello world!!!"<<endl; return 0; }
- When using cout standard output (console) and cin standard input (keyboard), the < iostream > header file and std standard namespace must be included.
- It is more convenient to use c + + input and output without adding data format control, such as shaping –% d, character –% c
1.2 default parameters
The default parameter is to specify a default value for the parameter of the function when the function is declared or defined. When calling the function, if no argument is specified, the default value is adopted; otherwise, the specified argument is used.
void TestFunc(int a = 0) { cout<<a<<endl; } int main() { TestFunc(); // When no parameter is passed, the default value of the parameter is used TestFunc(10); // When passing parameters, use the specified arguments }
2 namespace
The purpose of using namespace is to localize the name of identifier to avoid naming conflict or name pollution. The emergence of namespace keyword is aimed at this problem.
2.1 namespace definition
To define a namespace, you need to use the namespace keyword, followed by the name of the namespace, followed by a pair of {}, which are the members of the namespace.
2.2 namespace usage
There are three ways to use namespaces:
- Add namespace name and scope qualifier
int main() { printf("%d\n", N::a); return 0; }
- using to bring in members from a namespace
using N::b; int main() { printf("%d\n", N::a); printf("%d\n", b); return 0; }
- Use the using namespace namespace name to import
using namespce N; int main() { printf("%d\n", N::a); printf("%d\n", b); Add(10, 20); return 0; }
3 function overloading
3.1 concept
Function overloading: it is a special case of functions. C + + allows to declare several functions with the same name with similar functions in the same scope. The formal parameter list (number or type or order of parameters) of these functions with the same name must be different. It is commonly used to deal with the problems of similar functions and different data types.
4 references
4.1 quoted concepts
Instead of defining a new variable, a reference gives an alias to an existing variable. The compiler will not open up memory space for the referenced variable. It shares the same memory space with the variable it references.
Type & reference variable name (object name) = reference entity;
void TestRef() { int a = 10; int& ra = a;//< = = = = define reference type printf("%p\n", &a); printf("%p\n", &ra); }
- Reference to variable
char ch = 'A'; char &c = ch;
- Pointer reference
int *pa = &a; int * &qa = pa;
- Array reference
int arr[10] = {0,1,2,3,4,5,6,7,8,9}; int (&br)[10] = ar;
4.2 reference characteristics
-
References must be initialized when defined
-
A variable can have multiple references
-
Reference once an entity is referenced, other entities cannot be referenced
int x = 1; int &a = x; &a = y; //This statement is wrong
4.3 frequently cited
void TestConstRef() { const int a = 10; //int& ra = a; // An error will occur when compiling this statement. A is a constant const int& ra = a; // int& b = 10; // An error will occur when compiling this statement. B is a constant const int& b = 10; double d = 12.34; //int& rd = d; // There will be errors when compiling this statement. The types are different const int& rd = d; }
4.4 usage scenarios
- Make parameters
void Swap(int& left, int& right) { int temp = left; left = right; right = temp; }
- Do return value
int& Count() { static int n = 0; n++; // ... return n; }
4.5 comparison of value transfer and reference transfer efficiency
Taking value as a parameter or return value type, during parameter transfer and return, the function will not directly transfer the argument or return the variable itself, but transfer a temporary copy of the argument or return variable. Therefore, using value as a parameter or return value type is very inefficient, especially when the parameter or return value type is very large.
#include <time.h> struct A{ int a[10000]; }; void TestFunc1(A a){} void TestFunc2(A& a){} void TestRefAndValue() { A a; // Take value as function parameter size_t begin1 = clock(); for (size_t i = 0; i < 10000; ++i) TestFunc1(a); size_t end1 = clock(); // Take reference as function parameter size_t begin2 = clock(); for (size_t i = 0; i < 10000; ++i) TestFunc2(a); size_t end2 = clock(); // Calculate the time after the two functions run respectively cout << "TestFunc1(A)-time:" << end1 - begin1 << endl; cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl; }
4.5.2 performance comparison of values and references as return value types
#include <time.h> struct A{ int a[10000]; }; A a; // Value return A TestFunc1() { return a;} // Reference return A& TestFunc2(){ return a;} void TestReturnByRefOrValue() { // Takes the value as the return value type of the function size_t begin1 = clock(); for (size_t i = 0; i < 100000; ++i) TestFunc1(); size_t end1 = clock(); // Take reference as the return value type of the function size_t begin2 = clock(); for (size_t i = 0; i < 100000; ++i) TestFunc2(); size_t end2 = clock(); // Calculate the time after the operation of two functions is completed cout << "TestFunc1 time:" << end1 - begin1 << endl; cout << "TestFunc2 time:" << end2 - begin2 << endl; }
Through the comparison of the above codes, it is found that the efficiency of passing value and pointer as passing parameter and return value type is very different.
4.5 difference between reference and pointer
Differences between references and pointers:
- References must be initialized when defined, and pointers are not required
- After a reference references an entity during initialization, it can no longer reference other entities, and the pointer can point to any entity of the same type at any time
entity - There is no NULL reference, but there is a NULL pointer
- Different meanings in sizeof: the reference result is the size of the reference type, but the pointer is always the number of bytes in the address space (4 bytes in 32-bit platform)
#include<iostream> using namespace std; void main() { int a[10] = { 0 }; int (&c)[10] = a; cout << sizeof(c) << endl; int p = 10; int* pb = &p; cout << sizeof(pb) << endl; double b = 10; double* qb = &b; cout << sizeof(qb) << endl; }
- Reference self addition means that the referenced entity increases by 1, and pointer self addition means that the pointer is offset by one type backward
- There are multi-level pointers, but there are no multi-level references
#include<iostream> using namespace std; void main() { int a = 10; int* p = &a; int** pp = &p;//Multi level pointer int& ra = a; int&& rra = ra;//There are no multi-level references }
- The access to entities is different. The pointer needs to be explicitly dereferenced, and the reference compiler handles it by itself
- References are relatively safer to use than pointers
5. Inline function
5.1 concept
The function decorated with inline is called inline function. During compilation, the C + + compiler will expand where the inline function is called, without the overhead of function stack. The inline function improves the efficiency of program operation.
5.2 characteristics
- Inline is a method of exchanging space for time, which saves the cost of calling functions. Therefore, functions with long code or loop / recursion are not suitable for inline functions.
- Inline is only a suggestion for the compiler. The compiler will optimize automatically. If there are loops / recursions in the function defined as inline, the compiler will ignore inline during optimization.
- Inline does not recommend the separation of declaration and definition, which will lead to link errors. Because the inline is expanded, there will be no function address, and the link will not be found.
6. auto keyword (C++11)
6.1 introduction to auto
In the early C/C + +, the meaning of auto was that the variable modified by auto was a local variable with automatic memory. In C++11, the standards committee gave a new meaning to auto, that is, auto is no longer a storage type indicator, but a new type indicator to indicate the compiler. The variables declared by auto must be derived by the compiler during compilation.
int TestAuto() { return 10; } int main() { int a = 10; auto b = a; auto c = 'a'; auto d = TestAuto(); cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; cout << typeid(d).name() << endl; //auto e; Unable to compile, variables must be initialized when using auto to define them return 0; }
[note]
When using auto to define variables, it must be initialized. In the compilation stage, the compiler needs to deduce the actual type of auto according to the initialization expression. Therefore, auto is not a "type" declaration, but a "placeholder" for type declaration. The compiler will replace Auto with
The actual type of the variable.
6.2 usage rules of Auto
- auto is used in conjunction with pointers and references
When declaring a pointer type with auto, there is no difference between auto and auto *, but when declaring a reference type with auto, you must add&
int main() { int x = 10; auto a = &x; auto* b = &x; auto& c = x; cout << typeid(a).name() << endl; cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; *a = 20; *b = 30; c = 40; return 0; }
- Define multiple variables on the same line
When multiple variables are declared on the same line, these variables must be of the same type, otherwise the compiler will report an error, because the compiler actually deduces only the first type, and then uses the derived type to define other variables.
void TestAuto() { auto a = 1, b = 2; auto c = 3, d = 4.0; // This line of code will fail to compile because the initialization expression types of c and d are different }
6.3 scenarios that cannot be deduced by auto
- auto cannot be an argument to a function
- auto cannot be used directly to declare arrays
- In order to avoid confusion with auto in C++98, C++11 only retains the use of auto as a type indicator
- The most common advantage of auto in practice is to use it in combination with the new for loop provided by C++11 and lambda expression, which will be mentioned later.
7. Range based for loop (C++11)
7.1 syntax of scope for
In C++98, if you want to traverse an array, you can do it in the following way
void TestFor() { int array[] = { 1, 2, 3, 4, 5 }; for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i) array[i] *= 2; for (int* p = array; p < array + sizeof(array)/ sizeof(array[0]); ++p) cout << *p << endl; }
For a set with a range, it is superfluous for programmers to explain the range of loops, and sometimes they are easy to make mistakes. Therefore, a range based for loop is introduced in C++11. The parentheses after the for loop are divided into two parts by the colon ":": the first part is the variables used for iteration in the range,
The second part represents the iterative range
7.2 scope and conditions of use
- The scope of the for loop iteration must be deterministic
For an array, it is the range of the first element and the last element in the array; For classes, you should provide methods of begin and end, which are the scope of the for loop iteration.
Note: the following code is problematic because the scope of for is uncertain
void TestFor(int array[]) { for(auto& e : array) cout<< e <<endl; }
8. Pointer null nullptr(C++11)
8.1 pointer null value in C + + 98
In good C/C + + programming habits, when declaring a variable, it is best to give the variable an appropriate initial value, otherwise unexpected errors may occur, such as uninitialized pointers. If a pointer does not have a legal point, we basically initialize it as follows:
void TestPtr() { int* p1 = NULL; int* p2 = 0; // ...... }
be careful:
- When nullptr is used to represent pointer null value, it is not necessary to include header file, because nullptr is introduced by C++11 as a new keyword.
- In C++11, sizeof(nullptr) and sizeof((void*)0) occupy the same number of bytes.
- In order to improve the robustness of the code, it is recommended to use nullptr when representing pointer null values later.