[Elementary C++ (3)] Reference & inline function & auto keyword

Table of Contents

Preface

1. Quote

1.1 The concept of citation

1.2 Characteristics of references

1.3 Permission to quote

1.4 Use of references

1.5 The difference between references and pointers

2. Inline functions

2.1 What is an inline function?

2.2 Characteristics of inline functions

3. auto keyword

3.1 auto introduction

3.2 Auto usage rules

3.3 Scenarios where auto cannot be used

4. Range-based for loop

4.1 Scope for use

4.2 Conditions of use

5. C++ null pointer

Summarize


Preface

When learning C language, everyone may be embarrassed by pointers. There are also various problems when using pointers, such as the null pointer and wild pointer problem (pointers can point to any address at any time, including invalid addresses). In addition, when calling functions in C language, if the same function is called multiple times, creating a large number of function stack frames will lead to performance degradation. C++ has optimized and improved these shortcomings. So the “protagonist” of this issue is the reference & inline function.

1. Quote

1.1 The concept of citation

Reference is an important concept in C++. It is an alias for an existing variable. Reference is not a newly defined variable. The compiler will not allocate memory for reference variables. Space, it shares the same memory space as the variableit refers to. The operation on the reference is actually the operation on the original variable.

The reference is defined as: type & amp; reference name = original variable name;

for example:

int main()
{
int a = 1;
int & b = a;
a + + ;
b + + ;//b + + is also a + +

cout << a << endl;//output 3
return 0;
}

Note: The reference type must be of the same type as the referenced entity.

1.2 Referenced Characteristics

  • References must be initialized
  • Does not occupy additional space (the variables it refers to share the same memory space)
  • The binding of a reference cannot be modified (once a reference refers to one entity, it cannot refer to other entities)
  • References can be used as function parameters and return values
int main()
{
int a = 1;
int b = a;
\t
//int & amp; t; an error occurred

int & c = a;
int & d = a;
int & e = c;

//Same address
cout << &a << endl;
cout << &c << endl;
cout << & amp;d << endl;
cout << &e << endl;
return 0;
}

1.3 Quoted Permissions

In C++, reference permissions are similar to pointer permissions and can be divided into two types of permissions: constant references and non-const references.

Constant reference: A reference modified with const is called a constant reference. A constant reference can only read the value of the referenced variable, but cannot modify the value of the referenced variable.

If the referenced entity is modified with const, the reference must also be modified with const. That isPermissions can be reduced, but cannot be enlarged.

for example:

int main()
{
const int a = 1;
//int & amp; b = a; an error occurs and the permissions are enlarged
\t    
     //Permissions need to be increased
    const int a = 1;
    const int & b = a;

    //Permissions can be reduced
    int c = 2;
    const int & d = c;

return 0;
}

In addition, temporary variables created during type conversion also have constant attributes.

int main()
{
\t
int i = 1;
double j = i;

//double & amp; rj = i;//error report
//In the process of type conversion
//There is implicit conversion to generate temporary variables, and the temporary variables here have constancy
const double & amp; rj = i;

return 0;
}

1.4 Use of quotations

Make parameters

void Swap(int & amp; x, int & amp; y)
{
int tmp = x;
x = y;
y = tmp;
}

Compared with pointers

void Swap(int* x, int* y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}

It is more concise to use, and the value-passing efficiency of the reference is also very high. For example, when we pass a value, we pass a larger array. The pass-by-value call converts a large memory array into an array address and passes it over (it becomes 4 bytes).

struct A{ int a[10000]; };
void Func1(A a){}
void Func2(A & amp; a){}

What these pointers can do, references can do too.

Make return value

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

Before that, let’s first understand the return by value:

int Count()
{
int n = 0;
n + + ;

return n;
}

int main()
{
int ret = Count();

cout << ret << endl;
    return 0;
}

Is the n returned here the n in the Count function? The answer is no. In C language functions, the variables created within the function will be destroyed when the function execution ends, so what is returned here is a copy of the value of variable n in the Count() function (the value of n (temporary variable) is returned, not n this variable).

int & Count()
{
int n = 0;
n + + ;

return n;
}

If a reference is used as the return value, what is returned is the alias of n, which can also be understood as what is returned is the variable n (a warning will be reported: the address of the local variable or temporary variable is returned: n). It is very dangerous to directly return the variables in the function in this way, because n in the function is destroyed when it leaves the function scope, and then the data at the original n address is returned. At this time, the data is uncontrollable.

int main()
{
int & amp; ret = Count();

cout << ret << endl;//The first output is still the normal value 1

cout << ret << endl;//The second output becomes a random value
}

1.5 The differences between references and pointers

In the grammatical concept, a reference is an alias. It has no independent space and shares the same space with the entity it refers to.

int main()
{
int a = 10;
int & ra = a;
cout << " & amp;a = " << & amp;a << endl;//The output addresses are the same
cout << " & amp;ra = " << & amp;ra << endl;
return 0;
}

In the underlying implementation, there is actually space, because references are implemented in the form of pointers

Below is a comparison of the underlying assembly of references and pointers:

The difference between references and pointers:

  1. Reference conceptually defines an alias of a variable, and a pointer stores a variable address.
  2. References must be initialized when defined, pointers are not required
  3. After a reference refers to an entity during initialization, it can no longer refer to other entities, and a pointer can point to any entity of the same type at any time
  4. There is no NULL reference, but there is a NULL pointer
  5. The meaning in sizeof is different: the reference result is the size of the reference type, but the pointer is always the number of bytes occupied by the address space (32
    4 bytes under bit platform)
  6. Reference self-increment means that the referenced entity is increased by 1, and pointer self-increment means that the pointer is offset backward by the size of the type
  7. Multi-level pointers, but no multi-level references
  8. The way to access entities is different. The pointer needs to be explicitly dereferenced, and the reference compiler handles it by itself
  9. References are relatively safer to use than pointers

2. Inline function

2.1 What is an inline function

Functions modified with inline are called inline functions. When compiling, the C++ compiler will expand the inline function at the place where it is called. There is no overhead of establishing a stack frame for function calls. Inline functions improve program execution. efficiency.

As shown in the figure above, the function does not create a function stack frame when it is called, but is expanded directly inside the main function.

2.2 Characteristics of inline functions

  • Inline is a method of exchanging space for time. If the compiler treats a function as an inline function, during the compilation phase, the function call will be replaced with a function body

Disadvantages: The target file may become larger,

Advantages: less calling overhead and improved program running efficiency.

But not all functions need to be expanded, and inline function expansion only applies to smaller functions.

for example:

A function has 100 lines of code. It has been called 1000 times and expanded each time. That is 100,000 lines of code. How big will the generated file be? If it is a function stack frame call, each call will go to the same space to call the function. Just 100 more lines of code.

  • Inline is just a suggestion for the compiler. Different compilers may have different implementation mechanisms of inline. The general suggestion is: make the function smaller (that is, the function is not very long. There is no accurate statement, and it depends on the internal implementation of the compiler. ), non-recursive, and frequently called functions are modified with inline, otherwise the compiler will ignore the inline feature

Inline functions just send a request to the compiler, which the compiler can choose to ignore

  • Inline functions cannot be used when declaration and definition are separated

Separation can cause linking errors. Because inline is expanded, there is no function address and the link will not be found.

When the declaration and definition of an inline function are separated, an error will be reported if called directly, but it can be called indirectly.

//fun.h
inline void fun()
{
cout << "hello ,world!" << endl;
}

//fun.c
void func()
{
fun();
}

//test.c
int main()
{
fun();
return 0;
}

This situation can be called normally.

3. auto keyword

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

  • Type is difficult to spell
  • Unclear meaning leads to error-prone

3.1 auto introduction

Variables modified with auto are local variables with automatic storage, but are rarely used in daily life.

In C++11, the standards committee has given auto a new meaning: auto is no longer a storage type indicator, but serves as a new type indicator to instruct the compiler. Variables declared by auto must be compiled by the compiler. Derived from the period.

for example:

int Test()
{
return 10;
}
int main()
{
int a = 10;
auto b = a;
auto c = 'a';
auto d = Test();
cout << typeid(b).name() << endl;//int
cout << typeid(c).name() << endl;//char
cout << typeid(d).name() << endl;//int
\t
return 0;
}

auto automatically reads the type, and variables must be initialized when using auto to define them.

Note:

When using auto to define a variable, it must be initialized. During the compilation phase, the compiler needs to deduce the actual type of auto based on the initialization expression. Therefore, auto is not a “type” declaration, but a “placeholder” when the type is declared. The compiler will replace auto with the actual type of the variable.

3.2 auto usage rules

  • Use with pointer references

When using auto to declare a pointer type, there is no difference between using auto and auto*, but when using auto to declare a reference type, you must add & amp;

int main()
{
int x = 10;
auto a = &x;
auto* b = &x;
auto & c = x;
cout << typeid(a).name() << endl;//int*
cout << typeid(b).name() << endl;//int*
cout << typeid(c).name() << endl;//int

return 0;
}
  • Defining multiple variables in one 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
The compiler actually only deduces the first type, and then uses the deduced type to define other variables.

auto a = 1, b = 2;
auto c = 3, d = 4.0; // This line of code will fail to compile because the initialization types of c and d are different

3.3 Scenarios where auto cannot be used

  • auto cannot be used as a function return type

The latest C++ syntax is available, but it is not recommended. You must not write like this in daily application scenarios. In larger projects, most people collaborate. If you use auto, others are calling you. It is difficult to create a function interface without knowing the return type. If others also use auto, this will lead to a series of chains. In the end, the code will become more and more “hilly”. This is a typical “trap for teammates”. We must develop good habits. Coding style.

  • auto cannot be used as a parameter of a function
void TestAuto(auto a)//An error occurs and the compiler cannot deduce the actual type of a
{

}
int main()
{
TestAuto(2);
}
  • auto cannot declare arrays directly

4. Range-based for loop

4.1 Scope for use

int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
    //Normal for loop traversal
for (int i = 0; i < sizeof(arr) / sizeof(int); i + + )
{
cout << arr[i] << ' ';
}
cout << endl;
    //Range for traversal
for (int e : arr)
{
cout << e << ' ';//e here is a copy of the array value. Changing e cannot change the data of the array.
}
return 0;
}

For a ranged collection, it is redundant and sometimes error-prone for the programmer to specify the range of the loop. So range-based for loops were introduced in C++11. The brackets after the for loop are divided into two parts by the colon “:”: the first part is the variable in the range used for iteration, and the second part represents the range being iterated.

Note: Range for is 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.

4.2 Conditions of use

  • The range of for loop iteration must be determined
void TestFor(int array[])
{
  for(auto & e : array)
    cout<< e <<endl;
}

For example, in the above code, the scope is not clear. For an array, it is the range of the first element and the last element in the array; for a class, the begin and end methods should be provided, and begin and end are the range of the for loop iteration.

5. C++ null pointer

In C language, we commonly use NULL to represent a null pointer. NULL is actually a macro. In the 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

As you can see, NULL may be defined as a literal constant 0, or as a constant of an untyped pointer (void*). No matter what
With this definition, you will inevitably encounter some troubles when using null value pointers. We can test this using C++ code:

void f(int)
{
cout << "f(int)" << endl;
}
void f(int*)
{
cout << "f(int*)" << endl;
}
int main()
{
f(0); //f(int)
f(NULL); //f(int)
f((int*)NULL); //f(int*)
return 0;
}

It is not difficult to find that NULL has been replaced by 0 in C++. In C++98, the literal constant 0 can be either an integer number or an untyped pointer (void*) constant, but the compiler defaults to It will be regarded as an integer constant below. If you want to use it as a pointer, you must cast it to (void*) 0.

For the sake of safety, C++ introduced nullptr, which represents a null pointer. It is recommended to use nullptr when subsequently representing a pointer’s null value.

Summary

In this issue, some simple introductory syntax of C++ has been basically introduced. In the future, we will continue to study C++ in depth. The above is the entire content of this issue. Finally, thank you for reading!