C + + should learn with a smile: Reference&

Posted by twoeyes on Mon, 28 Feb 2022 05:34:31 +0100

πŸ”₯ πŸ”₯ πŸ”₯ πŸ”₯ πŸ”₯ Rush subscription πŸ‘‰    C + + learn with a smile     πŸ‘ˆ Interesting teaching blog πŸ”₯ πŸ”₯ πŸ”₯ πŸ”₯ πŸ”₯

Write before:

Ah, Hello, friends. I'm lemon leaf C. This chapter will explain the basis of C + + and quote some knowledge.

In some places, in order to deepen our understanding, we will give some interesting chestnuts, which will be properly adjusted while explaining.

I think this blog is the best one I have written so far. I hope you can see it to the end! If you think the article is good,

You can support bloggers with "one key three links"! Your attention is the biggest driving force for me to update! Thanks β™ͺ (ο½₯ ωο½₯) οΎ‰

(PS: the typesetting of the article has also been further upgraded to make it easier for everyone to see it on the mobile phone)

β… . Referenced concepts

0x00 introduce topic

I don't know if everyone knows the stem of "what's the relationship between catching Zhou Shuren and Lu Xun"

This is a line from Mr. Lu Xun in the 2018 TV series "Lou Wailou". In the play, an uneducated officer with a group of people came to arrest Zhou Shuren. Lu Xun asked them to take out the search warrant. They took it out. After reading it, Lu Xun said: what does the arrest of Zhou Shuren have to do with Lu Xun? So the group thought they had caught the wrong person and left.

This paragraph is actually mocking that they have no culture and don't even know the writer's pseudonym.

"What does the arrest of Zhou Shuren have to do with Lu Xun?" of course! Ha ha ha ha ha ha.

Later, the officer learned that Lu Xun was the pseudonym of Zhou Shuren, and the person they wanted to catch was Lu Xun.

πŸ”Ί This "pseudonym" is actually a quotation. Let's continue to learn.

0x01 what is a reference

πŸ˜‚ When you first come into contact with the concept of "quotation", you can understand it directly by looking at the words. It will really make people look confused

But if you use "alias" or "nickname" to understand, it is not so difficult to understand.

πŸ€” But if you use "alias" or "nickname" to understand, it is not so difficult to understand.

πŸ“š Concept: reference is to give an alias to an existing variable.

πŸ“œ Syntax: data type & reference name = reference entity;

                                πŸ‘† Here's & not an address! It is placed after the data type &, which must be distinguished!

πŸ’¬ Code demonstration:

#include <iostream>
using namespace std;

int main(void)
{
    int ZhouShuRen = 1881;
    int& LuXun = ZhouShuRen;  // Lu Xun was quoted by Zhou Shuren

    cout << ZhouShuRen << endl;
    cout << LuXun << endl;

    return 0;
}

🚩 Operation result: 1881

πŸ”‘ Interpretation:

The reference is at the grammar level. We should understand that there is no new space here, just a new name for the original.

πŸ“Œ matters needing attention:

β‘  A reference is not a new definition of a variable, but an alias for a variable.

β‘‘ The compiler will not open up memory space for the referenced variables. It will share the same memory space with the variables it references.

0x02 referenced properties

Reference must be initialized when defining!

During initialization, you must specify clearly who you want to alias.

Ambiguity doesn't work. You don't even know who you're going to alias. What are you doing with him?

πŸ’¬ Here comes everyone's favorite step on the pit:

#include<iostream>
using namespace std;

int main(void)
{
    int a = 10;
    int& b;      // ❌

    return 0;
}

A variable can have multiple references.

Of course, a person can have multiple nicknames, so a variable can also have multiple aliases.

πŸ’¬ Code presentation: (trump Chuan, Wang Jian Guo)

#include <iostream>
using namespace std;

int main(void)
{
    int Trump = 2333;           // variable
    int& ChuanJianGuo = Trump;  // Reference 1
    int& DongWang = Trump;      // Reference 2

    return 0;
}

Once a reference references an entity, it cannot reference other entities.  

int main(void)
{
	int a = 10;
	int& ra = a;

	int b = 20;
	ra = b;       // ?

	return 0;
}

(it's named ra here, because the quoted English is reference, so when I name the variable later, it will be abbreviated as r, or ref to represent reference)

❓ What does the question mark mean? Do you want ra to be the alias of b, or do you want to assign the value of b to ra?

πŸ’‘ Here is the assignment. Let's open the monitoring window and have a look:

πŸ”Ί The reference will not change. When we define it, whose alias it is, it is whose alias.

It won't change in the future. It's from the beginning to the end!!!

πŸ’¬ Reference and pointer are quite different. Pointer can change the direction:

int main(void)
{
    int a = 10;
    int* p1 = &a;

    int b = 20;
    p1 = &b;  // Change pointer pointing

    return 0;
}

πŸ”‘ Analysis: the pointer is like a scum man here!

πŸ“š Here again, the underlying layer of a reference is actually a pointer.

You can understand that he didn't want to be a scum man like that before, so he went back to the furnace and rebuilt it!

I'm not a scum man in rebirth. I only love one person in my life!

β…‘. Referenced application

0x00 introduction

It doesn't make sense to write like this:

int a = 10;
int& ra = a;

❗ What's really useful about it is that it can make parameters and return values.

0x01 reference as parameter

We talked about three ways of Swap two number exchange in C language teaching.  

What we used most at that time was to use temporary variables for exchange.

If you write it as a function, that's it:

void Swap(int* px, int* py) {
    int tmp = *px;
    *px = *py;
    *py = tmp;
}

int main(void)
{
    int a = 10;
    int b = 20;
    Swap(&a, &b);  // Byref 

    return 0;
}

Here, we need to pass the address when calling the Swap function, because the formal parameter is a temporary copy of the actual parameter, and changing the formal parameter will not have a substantive impact on the actual parameter.

πŸ’¬ But after we learn to quote, we can play like this:

Β Β 

void Swap(int& ra, int& rb) {
    int tmp = ra;
    ra = rb;
    rb = tmp;
}

int main(void)
{
    int a = 10;
    int b = 20;
    Swap(a, b);  // There is neither value nor address, but reference

    return 0;
}

πŸ” The monitoring results are as follows:

❓ How did you do the exchange?

πŸ”‘ As we know, formal parameters are defined in the stack frame.

When this function is actually called, ra and rb will be given space. When this function is called, the argument is passed to the formal parameter.

When did it start? It is defined when the argument is passed to the formal parameter.

ra is the alias of a and rb is the alias of b, so the exchange between ra and rb is the exchange between a and b.

Therefore, we can easily realize the exchange of two numbers by using this feature.

πŸ”Ί Let's sort it out and review the previous function overloading.  

πŸ’¬ Now we have learned three methods of parameter transmission: value transmission, address transmission and reference transmission.

#include<iostream>
using namespace std;

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

void Swap(int* px, int* py) {
    int tmp = *px;
    *px = *py;
    *py = tmp;
}

void Swap(int& rx, int& ry) {
    int tmp = rx;
    rx = ry;
    ry = tmp;
}

int main(void)
{
    int a = 10;
    int b = 20;

    Swap(&a, &b);
    Swap(a, b);  // report errors ❌  

    return 0;
}

Why does Swap(a, b) report an error here?

These three swaps can constitute function overloading,

As long as its function name modification rules are not affected, it will not be affected!

In other words, if the modified function names are different, overloading is supported!

voidΒ Swap(intΒ x,Β intΒ y);Β  Β  Β  Β  Β  Β  Β  _Z4swapixiy

voidΒ Swap(int*Β px,Β int*Β py);Β  Β  Β  Β _Z4swaprxry

voidΒ Swap(int&Β rx,Β int&Β ry);Β  Β  Β  Β _Z4swappxpy

However, there is ambiguity in the call of # Swap(a, b). Ambiguous call!

It doesn't know which one to call, whether to pass value or reference, so it will report an error.

At that time, when talking about the data structure single linked list, the secondary pointer was used, and the head node was not used at that time.

If you want to transmit the address of the pointer, you naturally need to receive it in the form of secondary pointer.

Now that we have learned about quotation, we can try to solve it by quotation (here we change. c to. cpp)

Any type can be aliased, and pointers are no exception:

int a = 10;
int& ra = a;

int* pa = &a;
int*& rpa = pa

Let's see how to implement it by reference!

πŸ’¬ SList.h:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SLNodeDataType;

typedef struct SingleListNode {
    SLNodeDataType data;           // Used to store node data
    struct SingleListNode* next;   // Pointer to the successor node
} SLNode;                          

void SListPrint(SLNode* pHead);
void SListPushBack(SLNode*& rpHead, SLNodeDataType x);
// ...  slightly

πŸ’¬ SList.cpp:

#include "SList.h"

/* Print */
void SListPrint(SLNode* pHead) {
    SLNode* cur = pHead;
    while (cur != NULL) {
        printf("%d -> ", cur->data);
        cur = cur->next;
    }
    printf("NULL\n");
}

/* Create a new node */
SLNode* CreateNewNode(SLNodeDataType x) {
    //Create and open up space
    SLNode* new_node = (SLNode*)malloc(sizeof(SLNode));
    //malloc check
    if (new_node == NULL) {
        printf("malloc failed!\n");
        exit(-1);
    }
    //place
    new_node->data = x; //Save incoming data
    new_node->next = NULL; //next is blank by default

    return new_node; //Submit new node
}

/* Tail insertion (reference of pointer) */
void SListPushBack(SLNode*& rpHead, SLNodeDataType x) {
    //Create a new node
    SLNode* new_node = CreateNewNode(x);
    //If the linked list is empty
    if (rpHead == NULL) {
        //Just insert it directly
        rpHead = new_node;
    }
    else {
        //Find tail node
        SLNode* end = rpHead;
        while (end->next != NULL) {
            end = end->next; //Make end point to the successor node
        }
        //insert
        end->next = new_node;
    }
}

πŸ”‘ Interpretation: slnode * & rphead here is an alias of pHead.

πŸ’¬ Test.cpp:

#include "SList.h"

// We won't pass the secondary pointer here.
//void TestSList1()
//{
//	SLNode* pList = NULL;
//	SListPushBack(&pList, 1);
//	SListPushBack(&pList, 2);
//	SListPushBack(&pList, 3);
//	SListPushBack(&pList, 4);
//
//	SListPrint(pList);
//}

// Method of using reference:
// We pass references to pointers!
void TestSList2()
{
	SLNode* pList = NULL;
	SListPushBack(pList, 1);
	SListPushBack(pList, 2);
	SListPushBack(pList, 3);
	SListPushBack(pList, 4);

	SListPrint(pList);
}


int main()
{
	TestSList2();

	return 0;
}

πŸ”‘ Interpretation: Here we use the reference method. When calling SListPushBack, the reference of pList is passed.

It is added here that in order to express it more simply, this writing method is also available in some books.

When defining the linked list structure, typedef defines one more pSLNode*

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SLNodeDataType;        // SLNodeDataType == int

typedef struct SingleListNode {
    SLNodeDataType data;           // int data used to store node data
    struct SingleListNode* next;   // Pointer to the successor node
} SLNode, *pSLNode; 

void SListPrint(pSLNode pHead);
void SListPushBack(pSLNode& rpHead, SLNodeDataType x);
// In this way, this kind of writing will appear πŸ‘†
// Many books are written like this, when we first talked about linked lists
// Because there was no C + + Teaching at that time, this method was not used.

0x02 value transfer return

Before we talk about reference return, we need to do a little foreshadowing.

πŸ’¬ Value transfer return:

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

int main(void)
{
    int ret = Add(1, 2);
    cout << ret << endl;
    
    return 0;
}

πŸ”‘ Interpretation:

Here, a temporary variable (c = 3) will be generated when return ing

Copy 3 to this temporary variable and return it to ret

If we give c directly to ret, there will be some problems.

If you directly take c to ret and get 3 or a random value, it depends on whether the stack frame destroys the space!

Strictly speaking, these visits are illegal.

Because this space has been returned to the operating system, it depends on the compiler.

Some compilers will be clear, some compilers will not be clear, which is too metaphysical!

Therefore, a temporary variable will be generated in the middle and submitted to ret.

Instead of directly using c as the return value, resulting in illegal access.

So instead of using c as the return value directly, a temporary variable is generated.

So the question is, where does this temporary variable exist?

β‘  If c is relatively small (4 or 8), it is generally a register to store temporary variables.

β‘‘ If c is large, the temporary variable will be placed in the stack frame where the Add function is called.

πŸ”Ί Summary: all returned values will generate a copy

(this is the mechanism of the compiler, just like passing values to participants to generate a copy)

Let's use VS to disassemble to deepen our understanding:

  πŸ”‘ Interpretation: we can clearly see that the result of a + b is indeed given to ret through the register.

0x03 reference as return value

We already know that a temporary variable will be generated here.

Now let's get back to the point. Let's try the return of quotation.

πŸ’¬ Experience the following code:

#include <iostream>
using namespace std;

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

int main(void)
{
    int ret = Add(1, 2);
    cout << ret << endl;

    return 0;
}

πŸ”‘ The meaning of reference return is to return the alias of c without generating temporary variables.

❌ Problems with this Code:

β‘  There is illegal access. Because the return value of Add(1, 2) is a reference to c, after the Add stack frame is destroyed, it will access the c location space.

β‘‘ If the stack frame of the Add function is destroyed and the space is cleared, the random value is obtained when taking the value of c, and the random value is given to ret. at present, this depends on the implementation of the compiler. Destroying stack frames under VS # is unclear for spatial data.

Since we don't know the spatial data, what are we worried about?

πŸ’­ Let's take a look at the following situation:

#include <iostream>
using namespace std;

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

int main(void)
{
    int& ret = Add(1, 2);
    cout << ret << endl;
    Add(10, 20);
    cout << ret << endl;  // Here ret becomes 30

    return 0;
}

🚩 Operation results:

πŸ”‘ Interpretation: we didn't move ret, but the result of ret became 30 because the stack frame was changed.

When you call Add again, the ownership of this stack frame is not yours.

When my function is destroyed, the stack frame is empty. The new function covers the previously destroyed stack frame,

So ret turns out to be 30.

It seems that it is still difficult to understand. In order to deepen the impression, I give an example of image (wonderful flower):

In fact, the operating system's management of memory space is like a landlord.

The memory we use is like renting a house from a landlord.

The stack frame is established. After the function call is completed, the house is returned to the landlord.

But you secretly left your suitcase in the room,

If no one happens to live in this room, you will have no problem picking up this suitcase.

But if someone lives and the new tenant doesn't move your suitcase, it won't be a problem.

I'm afraid the new tenant will lose your suitcase here, or even take your suitcase as his own.

Throw away all your clothes (data) and put your smelly socks in.

Then you go back to get your suitcase, and all you get is smelly socks, not your clothes.

🀣 Hula said a lot. The conclusion is: don't use reference return easily!

❓ What is the meaning of reference return? We'll talk about it later when we finish talking about classes and objects.

πŸ”Ί Summary:

It is not recommended to return by reference in daily life. If the scope of the function is out when the function returns,

If the returned object has not been returned to the operating system, it can be returned by reference. If it has been returned to the operating system,

Don't return by reference. Just pass the value honestly.

Generally speaking, if the returned object is still in the stack frame, you can use reference return.

πŸ’¬ For example: static variables and global variables will not be destroyed when they are out of scope

int& Count() {
    static int n = 0;
    n++;
    // ...
    return n;
}

πŸ“Œ Note: temporary variables are constant

#include <iostream>
#define N 10
using namespace std;


int& At(int i) {
    static int arr[N];
    return arr[i];  // Returns the reference (alias) of the ith of the array
}

int main(void)
{
    // write
    for (size_t i = 0; i < N; i++) {
        At(i) = 10 + i;  // 11, 12, 13, 14... At
    }

    // read
    for (size_t i = 0; i < N; i++) {
        cout << At(i) << " ";  // Get the value, but just print
    }
    cout << endl;

    return 0;
}

🚩 The operation results are as follows:

It is constant. The temporary variable is an R-value (which cannot be modified). It can be read but cannot be modified.

β…’. Discussion on quotation

0x00 compare the efficiency of value transfer and reference transfer

❓ What is the difference between value passing return and reference passing return?

πŸ’‘ Pass reference returns faster.

πŸ“š With the value as the parameter or return value type, during parameter passing and return, the function will not directly pass the argument or return the variable itself, but pass the argument or return a temporary copy of the variable.

Therefore, as a parameter or return value type, the efficiency is very low, especially when the parameter or return value type is very large.

Efficiency comparison of value transfer and reference transfer:

#include <iostream>
#include <time.h>
using namespace std;

struct S {
	int arr[10000];
};

void CallByValue(S a) {
	;
}

void CallByReference(S& a) {
	;
}

void TimeCompare() {
	S s1;

	/* Take value as function parameter */
	size_t begin1 = clock();
	for (size_t i = 0; i < 100000; i++) {
		CallByValue(s1);
	}
	size_t end1 = clock();

	/* Take reference as function parameter */
	size_t begin2 = clock();
	for (size_t i = 0; i < 100000; i++) {
		CallByReference(s1);
	}
	size_t end2 = clock();

	/* Calculate the time after the two functions run */
	cout << "Call by Value: " << end1 - begin1 << endl;
	cout << "Call By Reference: " << end2 - begin2 << endl;
}

int main(void)
{
	TimeCompare();

	return 0;
}

🚩 Operation results:

Performance comparison of value and reference as return value type:

πŸ’¬ Record the start time and end time to calculate the time after the completion of the two functions.

#include <iostream>
#include <time.h>
using namespace std;

struct S {
	int arr[10000];
};

void ByValue(S a) {
	;
}

void ReturnByReference(S& a) {
	;
}

void TimeCompare() {

	S s1;
	/* Take value as function parameter */
	size_t begin1 = clock();
	for (size_t i = 0; i < 100000; i++) {
		ByValue(s1);
	}
	size_t end1 = clock();

	/* Take reference as function parameter */
	size_t begin2 = clock();
	for (size_t i = 0; i < 100000; i++) {
		ReturnByReference(s1);
	}
	size_t end2 = clock();

	/* Calculate the time after the two functions run */
	cout << "Return By Value: " << end1 - begin1 << endl;
	cout << "Return By Reference: " << end2 - begin2 << endl;
}

int main(void)
{
	TimeCompare();

	return 0;
}

  🚩 The operation results are as follows:

Passing values and returning will create temporary variables, and 40000 byte s will be copied each time.

Since there is no copy returned by reference transfer, the speed will be much faster. Because it is a global variable, the stack frame will not be destroyed.

Therefore, in this scenario, we can use pass reference return to improve the running efficiency of the program.

πŸ”Ί Conclusion: there is a great difference in efficiency between value transmission and ship as transmission parameter and return value type.

The function of reference is mainly reflected in the transfer of parameters and return values:

β‘  Referring to pass parameter and return value can improve performance in some scenarios (large object + deep copy object).

β‘‘ Reference pass parameter and return value, output type parameter and output type return value.

In some scenarios, the parameters can be changed by changing the formal parameters.

In some scenarios, reference return can reduce copying and change the returned object. (learn about it and learn later)

Use a lot of references! Very important!

0x01 difference between reference and pointer

In terms of syntax concept: a reference is an alias. There is no independent space and shares the same space with its reference entity.

But in the underlying implementation: in fact, there is space, because the reference is implemented in the form of pointer.

#include <iostream>
#include <time.h>
using namespace std;

int main(void)
{
	int a = 10;
	int& ra = a;
	ra = 20;

	int* pa = &a;
	*pa = 20;

	return 0;
}

πŸ” Let's take a look at the assembly code comparison of reference and pointer:

0x02 difference between pointer and reference

Summary ❌ Whole life βœ…  

β‘  A reference conceptually defines the alias of a variable, while a pointer is the address where a variable is stored.

β‘‘ The reference must be initialized when defining. The pointer is best initialized, but there will be no error if it is not initialized.

int a = 0;
int& ra;    ❌ Must initialize!

int* pa;    βœ… Can not be initialized

β‘’ 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.

β‘£ There is a null pointer but no null reference.

β‘€ In sizeof, the meaning is different. The reference result is the size of the reference type, but the pointer is always the number of bytes in the address space (8 bytes in 64 bit platform)

#include <iostream>
using namespace std;

int main(void)
{
    int a = 10;
    int& ra = a;
    int* pa = &a;

    cout << sizeof(ra) << endl;
    cout << sizeof(pa) << endl;

    return 0;
}

🚩 The running results are as follows: (this machine is in 64 bit environment)

β‘₯ Reference + + means that the referenced entity is increased by 1, and pointer + + means that the pointer is offset backward by the size of one type.

#include <iostream>
using namespace std;

int main(void)
{
    int a = 10;
    int& ra = a;
    int* pa = &a;

    cout << "ra Before adding:" << ra << endl;
    ra++;
    cout << "ra After addition:" << ra << endl;

    cout << "pa Before adding:" << pa << endl;
    pa++;
    cout << "pa After addition:" << pa << endl;

    return 0;
}

🚩 The operation results are as follows:

⑦ There are multi-level pointers, but there are no multi-level references.

⑧ The access methods of entities are different. The pointer needs to be explicitly dereferenced, and the reference compiler handles it by itself.

#include <iostream>
using namespace std;

int main(void)
{
    int a = 10;
    int& ra = a;
    int* pa = &a;

    cout << ra << endl;    // References are handled directly by the compiler itself, that is, fetch and use.
    cout << *pa << endl;   // The pointer can only be obtained by adding and dereferencing operator *.

    return 0;
}

🚩 The operation results are as follows:

⑨ References are relatively safer to use than pointers.

πŸ”Ί Summary: pointers are more complex to use and more error prone. (the difference between pointer and reference is often investigated in interviews)

The use of pointers takes into account null pointers, wild pointers and other issues. Pointers are too flexible, so there is no reference security relatively!

β…£. Often cited

0x00 frequently cited concepts

If you want to use references to improve the efficiency of the program and protect the data passed to the function from being changed in the function, you should use constant references.

πŸ“œ Syntax: const data type & reference name = reference entity;

There are three situations in total: enlargement of permission, keeping permission unchanged and reduction of permission.

0x01 enlargement of authority

πŸ’¬ The following is a quoted example:

int main(void)
{
    int a = 10;
    int& ra = a;

    return 0;
}

πŸ’¬ If you use the {const modifier on the referenced entity, direct reference will result in an error:

int main(void)
{
    const int a = 10;
    int& ra = a;

    return 0;
}

🚩 The operation results are as follows: (error report)

πŸ”‘ Analysis: the reason for this problem is that I marked const, and the value in this space cannot be modified.

I can't modify myself. Your ra becomes a reference to my a, which means you ra can modify my a,

This is the enlargement of permissions. a is readable. You ra have to be readable and writable. Of course not.

If you can modify it casually, won't I be const lonely?

Is that reasonable? This is unreasonable!

❓ So how to solve this problem, let's continue to look.

0x02 keep the permissions consistent

Since the reference entity is decorated with const, my direct reference belongs to the enlargement of permission,

We can also prefix references with const to keep their permissions unchanged.

πŸ’¬ Prefix the reference with const:

int main(void)
{
    const int a = 10;
    const int& ra = a;

    return 0;
}

πŸ”‘ Interpretation: const int & RA = a means that I become your alias, but I can't modify you.

In this way, a is readable and not writable, and ra is also readable and not writable, so the permission remains unchanged.

If we want to use a reference but don't want it to be modified, we can use a constant reference to solve it.

0x03 reduction of authority

If the reference entity is readable and writable without const modification, but I hope its reference cannot modify it, we can use constant reference to solve it.

πŸ’¬ a is readable and writable, but I restrict ra to be readable and writable:

int main(void)
{
    int a = 10;
    const int& ra = a;

    return 0;
}

πŸ”‘ Interpretation: of course, this is the reduction of authority.

For example, if you apply for an ID card, your real name can be printed on the ID card,

But can your nickname be printed on your ID card?

So it needs to be restricted. Your nickname can be called, but it can't be written on your ID card.

Β Therefore, the reduction of authority can be understood as a kind of self-discipline.

0x04 frequently referenced applications

πŸ’¬ for instance:

Suppose x is a large object or a deep copy object learned later

Then try to pass parameters by reference to reduce copies.

If x does not need to be changed in Func function, try to use const reference to pass parameters here.

void Func(int& x) {
    cout << x << endl;
}

int main(void)
{
    const int a = 10;
    int b = 20;  

    Func(a);  // ❌  Error report, involving enlargement of authority
    Func(b);  // The permissions are consistent, no problem

    return 0;
}

Add const to keep the permissions consistent:

void Func(const int& x) {
    cout << x << endl;
}

int main(void)
{
    const int a = 10;
    int b = 20;  

    Func(a);  // Permissions are consistent
    Func(b);  // Reduction of authority

    return 0;
}

πŸ”‘ Interpretation: in this way, a is readable and non writable, and it is also readable and non writable when passed into Func function,

The permissions are consistent. b is readable and writable. Before const modification was used for formal parameters,

x is readable and writable, but when const is added, it belongs to the reduction of permission. x is readable but not writable.

I often quote that I will use more in the later stage. It doesn't matter if I don't understand it deeply now. It will happen sooner or later.

When we talk about classes and objects later, we will talk about them again and again, and the impression will continue to deepen.

0x05 reference of variable with constancy

πŸ’¬ First look at the code:

int main(void)
{
    double d = 3.14;
    int i = d;

    return 0;
}

d here can be given to i, which is called implicit type conversion in C language.

It will give the integer part of d to i and throw away the floating-point part directly.

❓ But what if I add a quote here?

int main(void)
{
    double d = 3.14;
    int& i = d;  // Can i quote d with i?

    return 0;
}

🚩 Operation result: (error report)

Using i to quote d directly will report an error. Think about why?

Some friends here may want to say that d is a floating point type and i is an integer type. Is it because of different types?

But the wonderful thing is, if I put a const modifier in front of it, but I don't report an error, why?

int main(void)
{
    double d = 3.14;
    const int& i = d;  // ???  Again

    return 0;
}

Hey, it * * drops! Const, const why!!!

πŸ”‘ Resolution: because {temporary variables are constant, they cannot be modified.

Implicit type conversion does not happen directly, but now a temporary variable is generated in the middle.

First give d to the temporary variable, and then give things to i:

If a reference is used here, the alias of the temporary variable is generated,

Because the temporary variable is an R-value and cannot be modified, an error is reported.

πŸ”Ί Conclusion: if the reference is a constant variable, use the reference with const.

0x06 constant reference as parameter

Pass parameters by reference. If the value of the parameter is not changed in the function, const is recommended&

πŸ’¬ for instance:

Generally, there is no need to change the parameter value for stack printing. const can be added here

void StackPrint(const struct Stack& st) {...}

const data type & can receive various types of objects.

There are many advantages of using const reference. If the const object is passed in, the permission remains unchanged;

Ordinary objects are the reduction of permissions; Temporary variables generated in the middle can also be solved.

Because const references take all, its value lies in this place. If const is not added, it can only be passed to ordinary objects.

Thank you for reading here!

reference material:

Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. .

Baidu Encyclopedia [EB / OL] []. https://baike.baidu.com/.

Bit technology C++[EB/OL]. 2021[2021.8.31]. .

Programmer interview DICTIONARY [M] 5. .

πŸ“Œ Author: Wang Yiyou

πŸ“ƒ Update: 2022.2.28 (Revised Version)

❌ Corrigendum: None

πŸ“œ Statement: due to the limited level of the author, it is inevitable that there are errors and inaccuracies in this article. I also want to know these errors. I sincerely hope the readers can criticize and correct them!

End of this chapter.

Topics: C++