[C++] Detailed introduction to C++ II [Introduction to C++ in simple terms, this article is enough]

Getting Started with C++



7. Quote

(1) Quote concept

(1) Quote concept

Reference does not define a new variable, but gives an alias to an existing variable< /span>.
The compiler will not allocate memory space for the reference variable, it shares the same memory space with the variable it refers to
Insert image description here

It's like Lu Xun and Zhou Shuren are the same person.



(2) Quote use

To quote is to take an alias;

Type& Reference variable name (object name) = reference entity;
【★☆★ Note: The reference type must be of the same type as the reference entity ’s]

在这里插入代码片


★☆(3) Quote characteristics

  1. references must be initialized when is defined
    Insert image description here

  2. A variable can have multiple references
    An alias can also be aliased

Insert image description here
Constants can also be aliased
Insert image description here

  1. ReferenceOnce an entity is referenced, it cannot reference other entities
    Insert image description here
    Once an alias is defined for one entity, it cannot be changed to another entity. Another name.
  • The = symbol cannot be used in C++ to change the alias obtained by referencing one entity to the alias of another entity.
  • But in Java, you can change the direction of the reference of the alias through the = symbol.

So,aliases cannot completely replace pointers, more aliases< a i=4> Used with pointers



(4) Often cited

//常引用
int main(){
    
    
   const int a = 10;
   int& b = a;
   
   return 0;
}

You can run this code under your own compiler to see what problems will occur.

Detailed explanation
Insert image description here



  • const modification:

    1. Aliases for variables that can be modified with const (permissions can be shifted)
    2. You can also make aliases for non-const modified variables (permissions can be narrowed)
  • pointers also have the problem of permission enlargement and reduction:
    const + pointer cannot be passed to a non-const pointer for storage. (Under the const modification, there is still the possibility of modification, which is illegal)

  • Summary:
    Permissions can be panned and reduced, but cannot be enlarged.

Insert image description here
Here, another variable space b is defined, and the value of a is assigned to b. The copy of the value has no effect on the permissions of a, so it is legal.



(2) Practical applications and significance of quotations

It is precisely because of the feature that the alias and the variable it refers to share the same memory space, soGetting an alias is just like passing the address. The changed memory space is the same block. For the following article, when making parameters, the efficiency of writing code is greatly improved [please see below]



☆ (1) Making parameters - & aliasing the formal parameters can be done. Changes in the formal parameters can affect the actual parameters.


☆★ 1.1 Comparison of efficiency of passing by value and passing by reference

  • Passing by value
    uses value as parameter or return value type, during parameter passing and return, The function does not directly pass actual parameters or directly return the variable itself , but Pass actual parameters or return a temporary copy of the variable.

Thereforeusing value as parameter or return value type is very inefficient, especially whenWhen the parameter or return value type is very large, the efficiency will be lower.

  • Passing by reference = passing pointer (and it is more convenient than passing pointer)
    It is precisely because the alias and the variable it refers to share the same block Memory space has this feature, so taking an alias is just like passing the address, and the changed memory space is the same .
    Therefore, alias the parameter passed by (as a formal parameter) = dereference the address of * actual parameter< /span>, directly operates on the address of the actual parameter, without copying the content.
    Insert image description here


1.2 Passing by value and passing parameters by reference Performance test comparison

Insert image description here



(2) Make return value

Question 1: What result does the following code output? Why?

int& Add(int a, int b)
{
    
    
    int c = a + b;
    return c;
}

int main()
{
    
    
    int& ret = Add(1, 2);
    cout << "Add(1, 2) is :"<< ret <<endl;
    
    Add(3, 4);
    cout << "Add(1, 2) is :"<< ret <<endl;
    return 0;
}

The answer is 3,7;


parse
Insert image description here



★ Discussion: When the function is passed by reference and returns a value, the stack frame is destroyed after the function is called. Can the data still be found after the space usage rights are recovered?

  • In return by value , what is passed back is a copy of n value
  • And inreturn by reference, it is equivalent to referencing n with the alias tmp (The alias and the entity share the same space and have the same address). Passing tmp back is equivalent to passing n back (because the address is the same).

☆ But there will be problems similar to wild pointers. Because after the function call is completed, the stack frame opened by the function is destroyed, and the right to use the space is taken back by the operating system, but the space Still, when the operating system continues to run the program, it will reallocate this space and reuse it.

So what is returned depends on:

  1. Whether the compiler clears the space of n when destroying the stack frame
  2. And, whether the space usage rights will be allocated to the next program after the space usage rights are recovered, and whether the data generated by the program operation will overwrite the value stored in the original space variable n to be returned.
    Insert image description here
    Insert image description here


[The calling and destruction of function stack frames Students who do not understand are recommended to read this article to understand its underlying principles, which can help We better grasp knowledge and connect knowledge together. 】

Insert image description here




Improvement: Use static to modify it. When it goes out of scope, the object is still there (legalized) before it can be referenced and returned.

  • Question 2: If the condition is changed to static int c = a + b; and what will happen to the result?
int& Add(int a, int b)
{
    
    
    static int c = a + b;        //局部静态变量,只会被初始化一次
    return c;
}

int main()
{
    
    
    int& ret = Add(1, 2);
    cout << "Add(1, 2) is :"<< ret <<endl;
    
    Add(3, 4);
    cout << "Add(1, 2) is :"<< ret <<endl;
    return 0;
}
  • Question 3: What is the value of static int c; c = a + b;?
int& Add(int a, int b)
{
    
    
    static int c;
    c = a + b;
    return c;
}

int main()
{
    
    
    int& ret = Add(1, 2);
    cout << "Add(1, 2) is :"<< ret <<endl;
    
    Add(3, 4);
    cout << "Add(1, 2) is :"<< ret <<endl;
    return 0;
}

Insert image description here



★☆ Discussion: Temporary variables

(1) Type conversion [1. Type promotion, 2. Type truncation, 3. Forced type conversion] all generate temporary variables.

1.1 Type promotion

After the x type is promoted, does it become unsigned? No, it has no effect on x itself.
Insert image description here

1.2 Type truncation

All are done by truncating the temporary variable of ii, and then using the truncated temporary variable to assign a value to ch. It has no effect on its original variable.
Insert image description here

1.3 Forced type conversion

Insert image description here

(2) Temporary variables must be generated when passing values, passing parameters, and returning values.

2.1 Passing parameters

The formal parameter is a temporary copy of the actual parameter

2.2 Return by value

Insert image description here

All temporary variables are generated, and the generated temporary variables are processed and have no impact on the original variables.


3. Has a change in time Normal,Impossible to change

Insert image description here



(3) Performance comparison between return by value and return by reference

#include <time.h>
struct A{
    
     int a[10000]; };
A a;

// 值返回
A TestFunc1() {
    
     return a;}
// 引用返回
A& TestFunc2(){
    
     return a;}
void TestReturnByRefOrValue()
{
    
    

 // 以值作为函数的返回值类型
 size_t begin1 = clock();
 for (size_t i = 0; i < 100000; ++i)
 TestFunc1();
 size_t end1 = clock();
 // 以引用作为函数的返回值类型
 size_t begin2 = clock();
 for (size_t i = 0; i < 100000; ++i)
 TestFunc2();
 size_t end2 = clock();
 
 // 计算两个函数运算完成之后的时间
 cout << "TestFunc1 time:" << end1 - begin1 << endl;
 cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

Through the comparison of the above codes, it was found that The efficiency of passing values ​​and pointers as parameters and return value types is very different



★The difference between reference and pointer

  • In grammatical concept, a reference is an alias. It has no independent space and shares the same space with its referenced entity.

Insert image description here


  • There is actually space in the underlying implementation, becausereferences are implemented in pointer mode .

Insert image description here

[ In actual operation, syntax is still the main focus (although its underlying implementation is implemented by pointers): a>. 】The reference does not open space; the pointer opens space



Summary: The differences between references and pointers (differences in usage and concepts)

  1. ReferenceConceptually defines an alias of a variable, and the pointer stores a variable address.

  2. References must be initialized when defined, but there is no requirement for pointers (wild pointer problems are prone to occur)

  3. After a reference refers to an entity during initialization, it can no longer refer to other entities, and The pointer can point to any object of the same type at any time Entity

  4. There is no NULL reference, but there is a NULL pointer
    [3 and 4 points: References are safer, but they are not absolutely safe]< /span>

  5. has different meaning insizeof: reference The result is The size of the reference type, but the pointer is always the number of bytes occupied by the address space(4 bytes on 32-bit platform)

  6. The reference is added, that is, the entity referenced by is increased by 1, The pointer increments, that is,the pointer is offset backward by the size of the type

  7. Multi-level pointers, butno multi-level references

  8. The way to access entities is different, pointers need to be explicit Dereferencing*,referencing is handled by the compiler itself

  9. References are relatively safer to use than pointers

Insert image description here
Insert image description here



8. Inline functions

(1) Preface: Macros in C

Because when the program is running, the function to establish a stack frame is a very expensive thing.

  1. save register
  2. Make space for registers

To solve this problem, C language introduced the concept of macro.

[If you want to know more about macros, there are no updates yet, so stay tuned, I will put the article link here]


(2) Advantages of macros

  1. Enhance code reusability
  2. Macros are similar to functions: direct replacement, no need to create a stack frame, improving efficiency

(3) Disadvantages of macros

  1. is not convenient for debugging macros. (Because has been replaced in the pre-compilation stage, and it has been compiled during debugging)

  2. The content to be replaced is at a certain distance from the position to be replaced. There is no way to check it as intuitively as writing it directly in the program. You cannot directly check the problems in the code at a glance.
    leads to poor code readability, poor maintainability, and easy misuse.

  3. No type safety checks



Improvements made by C++ to address the shortcomings of macros: Use enum const inline inline functions instead of macros

(1) enum const replaces macro constant

(2) inline replaces macro function



inline function

(1) Concept

The function modified withinline is calledinline function , When compiling, the C++ compiler will call the inline function where Expanding , does not have the overhead of establishing a stack frame through function calls, and inline functions improve the efficiency of program operation.
Insert image description here

After inline modification, when compiling, the C++ compiler will expand where calls the inline function
Insert image description here



Inline expansion View by:

  1. Right click <file name>

  2. Click <Properties>

  3. Inrelease mode, check whether call Add exists in the assembly code generated by the compiler [Under default conditions, the function will still create a stack Frame]

  4. Indebug mode, the compiler needs to be set, otherwise it will not expand
    (because In debug mode, the compiler will not optimize the code by default. The following is the setting method for vs2013)
    Insert image description here


(2) Characteristics

  1. inline is a method of exchanging space for time. If the compiler treats the function as an internal Linked function processing, during the compilation phase, function calls will be replaced with function bodies.
  • Defect: target file may become larger
  • Advantages:No need to create stack frames, improving program operation efficiency


  1. Inline is just a suggestion for the compiler, and different compilers may have different inline implementation mechanisms.
  • General advice:
  • willthe function size is smaller(that is, the function is not very long, there is no accurate statement, it depends on the internal implementation of the compiler, probably < /span> are modified inline,Functions that are frequently called, and is not recursive ), Within 10 lines
  • Otherwise the compiler will ignore the inline feature.

The picture below shows the suggestions on inline in the fifth edition of "C++prime":
Insert image description here



  1. inline does not recommend the separation of declarations and definitions, separation will cause link errors.
    Because inline is expanded (expanded directly in the program, no address is needed), there is no function address, and the link will not be found a>.
//声明和定义分离
// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{
    
    
 cout << i << endl;
}
// main.cpp
#include "F.h"
int main()
{
    
    
 f(10);
 return 0;
}
// 链接错误:main.obj : error LNK2019: 无法解析的外部符号 "void __cdeclf(int)" (?f@@YAXH@Z),该符号在函数 _main 中被引用


To summarize the advantages of inline functions:

  1. inline + function ——> inline function [easy to write, simple syntax]
  2. The efficiency is the same as that of a macro: no need to build a stack frame (it will be expanded (the compiler will call out the logic of the function))
  3. Can also debug

The shortcomings of macros can be overcome by inline functions.



9. auto keyword (C++11)

Preface: Thinking about type aliases

As the program becomes more and more complex, the types used in the program become more and more complex, often reflected in:

  1. Type is difficult to spell
  2. Unclear meaning leads to error-prone

typeid printing type

#include <string>
#include <map>
int main()
{
    
    
 std::map<std::string, std::string> m{
    
     {
    
     "apple", "苹果" }, {
    
     "orange",
"橙子" },
   {
    
    "pear","梨"} };
 std::map<std::string, std::string>::iterator it = m.begin();
 while (it != m.end())
 {
    
    
 //....
 }
 return 0;
}

For 3 characters like int and 4 characters like auto, the difference is not big and has no meaning.

  • The main use of auto is that when defining objects, the type is longer, so it is more convenient to use it:
    Insert image description here

std::map<std::string, std::string>::iteratorIt is a type, but the type is too long and it is easy to make mistakes.



typedef aliases the type

Smart students may have thought that you can use typedef to alias the type, for example:

#include <string>
#include <map>
typedef std::map<std::string, std::string> Map;
int main()
{
    
    
 Map m{
    
     {
    
     "apple", "苹果" },{
    
     "orange", "橙子" }, {
    
    "pear","梨"} };
 Map::iterator it = m.begin();
 while (it != m.end())
 {
    
    
 //....
 }
 return 0;
}

Using typedef to alias types can indeed simplify the code, but typedef will encounter new problems:

typedef char* pstring;
int main()
{
    
    
 const pstring p1;    // 编译成功还是失败?
 const pstring* p2;   // 编译成功还是失败?
 return 0;
}

When programming, it is often necessary to assign the value of an expression to a variable, which requires that the type of expression be clearly known when declaring the variable.. However, sometimes it is not so easy to do this, so C++11 gives auto a new meaning.



(1) Introduction to auto

  • In the early days of the meaning of auto in C/C++ is: variables modified with auto are There are local variables with automatic memory, but unfortunately no one has ever used it. Can you think about why?

  • In C++11, the standards committee gave auto a new meaning:auto is no longer a storage type indicator, but as a new type indicator. Instructs the compiler that variables declared with auto must be deduced by the compiler at compile time.

    • Therefore,When using auto to define a variable, it must be initialized. During the compilation phase, the compiler needs to deduce the value of auto based on the initialization expression. Actual type.
    • [Therefore, auto is not a "type" declaration, but a "placeholder" for type declaration< /span>. 】, the compiler will replace auto with the actual type of the variable 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; 无法通过编译,使用auto定义变量时必须对其进行初始化
return 0;
}

(2) Detailed rules for using auto

  1. auto and pointers
    When using auto to declare a pointer type, there is no difference between using auto and auto*

  2. auto and reference
    When auto declares a reference type, 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;
}

Insert image description here

  1. Defining 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 An error will be reported. [Becausethe compiler actually only deduces the first type, and then uses the deduced type to define other variables. 】
void TestAuto()
{
    
    
    auto a = 1, b = 2;
    auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}

(3) Scenarios where auto cannot be deduced

  1. auto cannot be used as a function parameter, and function return value is not supported either

The code here failed to compile, auto cannot be used as a formal parameter type, becausethe compiler cannot deduce the actual type of a[The reason Same asauto initialization]. If the function is not called, then the formal parameter auto a; is equivalent to not being initialized

// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导。
//要是函数不进行调用了,那么这个形参auto a;就相当于没有被初始化了
void TestAuto(auto a) X
{
    
    }

int main(){
    
    
//TestAuto(5);
}

  1. Function return values ​​are also not supported
X 
auto TestAuto(auto a)
{
    
    }

int main(){
    
    
//TestAuto(5);
}

This is very confusing.
Insert image description here

  1. auto cannot be used directly to declare an array
void TestAuto()
{
    
    
    int a[] = {
    
    1,2,3};
    auto b[] = {
    
    456};
}
  1. In order to avoid confusion with auto in C++98, C++11 only retains the use of auto astype indicator

  2. The most common advantageous use of auto in practice is the new for loop provided by C++11 which will be discussed later a> etc. for use together. lambda expression, and



10. Range-based for loop (C++11)

Preface

In C++98, if you want to traverse an array, you can do it as follows:

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 aranged set, it is redundant for the programmer to specify the range of the loop, and sometimes it is easy to make mistakes. mistake. Therefore, range-based for loops were introduced in C++11.



(1) Syntax of range for

  • The brackets after the for loop are divided into two parts bycolon ":": the first part is the range The variable used for iteration, and the second part represents the range being iterated.
void TestFor()
{
    
    
int array[] = {
    
     1, 2, 3, 4, 5 };
for(auto& e : array)
     e *= 2;
for(auto e : array)
     cout << e << " ";
return 0;
}

[ Note: Similar to a normal loop, you can use continue to end this loop, or you can use break to jump out of the entire loop.



(2) The meaning of grammar

Take the values ​​​​in the array and assign them to e in sequence. The automatic judgment ends and automatic ++ goes back.



(3) Conditions for using scope for

  1. The range of for loop iteration must be certain

    For arrays, it is the range of the first element and the last element in the array; for classes, the begin and end methods should be provided, and begin and end are The range of for loop iteration.

Note: There is a problem with the following code because the scope of for is uncertain

void TestFor(int array[])
{
    
    
   //用auto:要是数组类型变了,这里也不用动类型
    for(auto& e : array)     //这里,auto& 即可改变数组array (指针无法替代)
        cout<< e <<endl;
    
   //也可以用实际类型    
    for(int& e : array)
        cout<< e <<endl;
}

Here, auto& can change the array (pointers cannot be replaced). Only & references can do this, not pointers.
Insert image description here


  1. The iterated object must implement ++ and == operations. (As for the issue of iterators, I will talk about it later. I will mention it now. There is no way to explain it clearly. Now everyone can understand it.)


11. Pointer null value nullptr (C++11)

Preface: 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 unpredictable errors may occur, such as uninitialized pointers. If a pointer does not have a legal pointer, we basically initialize it as follows:

void TestPtr()
{
    
    
int* p1 = NULL;
int* p2 = 0;
// ……
}
  • NULL is actually a macro, inthe traditional C header file (stddef.h) , you can see the following code:
#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

You can see that NULL may be defined as a literal constant 0, or as a constant of an untyped pointer (void *). No matter what definition is adopted, you will inevitably encounter some troubles when using null value pointers, such as:

void f(int)
{
    
    
 cout<<"f(int)"<<endl;
}
void f(int*)
{
    
    
 cout<<"f(int*)"<<endl;
}

int main()
{
    
    
//程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的初衷相悖。
//在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量。
 f(0);
 f(NULL);

//如果要将其按照指针方式来使用,必须对其进行强转(void*)0。
 f((int*)NULL);
 return 0;
}

The original intention of the program is to call the pointer version of the f(int*) function through f(NULL), but because NULL is defined as 0, it goes against the original intention of the program.



(1) Pointer null value nullptr (C++11)

  • In C++98, the literal constant 0 can be either an integer number or an untyped pointer (void *) constant, but the compiler treats it as an integer constant by default< /span>.

  • In order to make up for the shortcomings of C: NULL, C++ defines nullptr.
    Insert image description here


nullptr
Insert image description here


  • Notice:
  1. When using nullptr to represent a pointer null value, does not need to include the header file because a>nullptr was introduced as a new keyword in C++11.

  2. In C++11, sizeof(nullptr) and sizeof((void * ) 0) occupies the same number of bytes.

  3. In order toimprove the robustness of the code, it is recommended to use it later when represents a pointer null value. nullptr.

Guess you like

Origin blog.csdn.net/NiNi_suanfa/article/details/134282345