[C + +] simple record of the reason for introducing the "reference" mechanism

Posted by maxat on Mon, 14 Feb 2022 20:34:43 +0100

πŸ“£πŸ₯³πŸ₯³πŸ₯³πŸ“£

✨ Hello! If this [article] is helpful to you πŸ˜„οΌŒ I hope I can praise the blogger πŸ‘ Encourage me 😘

πŸ“£πŸ₯³πŸ₯³πŸ₯³πŸ“£

πŸ€” Why introduce references?

In response to [why introduce references?] Before this problem, let's review the two call forms of functions: pass by value and pass by reference. In C language, address transmission can only be realized in the form of pointer.
Examples of value transfer methods are as follows πŸ‘‡

#include <iostream>
#include <vector>
using namespace std;

void swap(int x, int y);    // swap function declaration
void display(vector<int> vec);    // display function declaration

int main() 
{
	int num1 = 1, num2 = 2;
	swap(num1, num2);    //You want to swap the values of num1 and num2
    int num_arr[2] = {num1, num2};
    vector<int> num_vec(num_arr, num_arr+2);
	display(num_vec);
	return 0;
}

/* Function function: (imagine being able to) exchange the values of two objects of type int */
void swap(int x, int y) {
	int temp = x;
	x = y;
	y = temp;
}

/* Function function: print each value in a vector template class object */
void display(vector<int> vec) {
	for (int i = 1; i <= vec.size(); ++i) {
		cout << "num" << i << " = " << vec[i-1] << endl;
	}
}

The compilation results are as follows πŸ‘‡

num1 = 1
num2 = 2

It can be found that the expected results have not been achieved. This is because the value transfer method is only a simple copy when transferring parameters. When using the swap() function, the num1 and num2 objects passed in and the x and Y objects operated inside the swap() function actually have no other relationship except that the values are exactly the same. Therefore, no matter how to operate the x and y objects inside the function, it will not affect the num1 and num2 objects outside the swap() function. Therefore, we must implement it in the way of address transmission.
In addition, it should be noted that when we call the swap () function, a special area called the program stack will be established in memory. The program stack provides storage space for each function parameter, that is, the objects num1 and num2 are stored in the current special area. We call the objects located in this special area as local objects. Once the function body of swap() function is executed, this memory will be released (destroyed) immediately. Therefore, after the function is executed, the local objects x and y will also be destroyed.

For the address transmission mode, C + + not only can be implemented in the form of pointer, but also introduces the reference mechanism. Next, address transmission is realized in the form of pointer, and the code of the above example is changed as follows πŸ‘‡

#include <iostream>
#include <vector>
using namespace std;

void swap(int *x, int *y);    // With adjustment
void display(vector<int> vec);    

int main() 
{
	int num1 = 1, num2 = 2;
	swap(num1, num2);    
    int num_arr[2] = {num1, num2};
    vector<int> num_vec(num_arr, num_arr+2);
	display(num_vec);
	return 0;
}

void swap(int *x, int *y) {    // With adjustment
	int temp = *x;    // There are adjustments. After the address is transmitted in the form of pointer, it is necessary to manually solve the address when using it. Later, it will be found that there is no need to manually solve the address when using the reference mechanism
	*x = *y;    // There are adjustments.
	*y = temp;    // With adjustment
}

/* Function function: print each value in a vector template class object */
void display(vector<int> vec) {
	for (int i = 1; i <= vec.size(); ++i) {
		cout << "num" << i << " = " << vec[i-1] << endl;
	}
}

The compilation results are as follows πŸ‘‡

num1 = 2
num2 = 1

It is clear that the expected results have been achieved.

Next, use the reference mechanism to realize the address transmission mode. Just change the swap() function in the initial code as follows πŸ‘‡

void swap(int &x, int &y);   

The compilation results are as follows πŸ‘‡

num1 = 2
num2 = 1

It can be found that the expected effect can also be achieved.

[back to the question, since the pointer can realize address transmission, why introduce the reference mechanism to realize address transmission?] The thinking results are as follows πŸ‘‡

Compared with the pointer form, the reference mechanism does not need to manually use the de address operator, and it is very intuitive when calling the function. When modifying the function, the whole function body does not need to be adjusted at all. The introduction of the reference mechanism is specifically aimed at the needs of the address transmission scenario when the function is called. In this scenario, the reference mechanism can be used to replace the traditional pointer form, so as to avoid the troublesome problems of address resolution operation in the function body and function value transmission and address adjustment.

πŸ€” When to transmit value and address?

When defining a function, how to determine whether to pass a value or an address? The statement in Essential C + + is as follows πŸ‘‡

One reason for addressing is that you want to be able to modify the incoming object directly. This reason is extremely important. (for example, the swap exchange function mentioned above cannot achieve the expected requirements if it does not adopt address transmission.)
Another reason for addressing is the desire to reduce the additional burden of copying large objects. However, this reason is less important and involves the efficiency of the procedure.

For the second reason, you can use the print function display() of the above example to illustrate. This function is defined as follows πŸ‘‡

void display(vector<int> vec) {
	for (int i = 1; i <= vec.size(); ++i) {
		cout << "num" << i << " = " << vec[i-1] << endl;
	}
}

It can be found that the parameter transfer mode of the function is value transfer mode, which means that all elements in the vector will be copied every time the display operation is performed. If the address of the vector is directly passed in, that is, the address transmission method is adopted, for the vector with large capacity, the overhead can be reduced and the execution speed will be faster. Change as follows πŸ‘‡

void display(vector<int> &vec) {
	for (int i = 1; i <= vec.size(); ++i) {
		cout << "num" << i << " = " << vec[i-1] << endl;
	}
}

πŸ€” What is the specific difference between a reference and a pointer?

A brief summary is as follows πŸ‘‡

one ️⃣ A reference is a "restricted" pointer, which is safer. The reference definition must be initialized and only bound to the initial object as the alias of the object (while the pointer itself is an object. Using sizeof (Reference) gets the type size of the object it represents, while sizeof (pointer) gets the type size of the pointer object itself). Because it must be initialized and always bound to an object, there is no null reference, but the pointer can be changed at will (the pointer can also be understood as an ordinary variable, but its variable value is an address value, which is more special), Null pointers and wild pointers are allowed (wild pointers can be generated in a variety of scenarios. In one case, when multiple pointers point to a piece of memory and free drops one pointer, other pointers become wild pointers).

two ️⃣ About constant pointers, constant references, pointer constants, and reference constants
Constant pointer / constant reference means that it is impossible to reassign the variable pointed to / represented by this pointer / reference. Examples are as follows πŸ‘‡

int main() {
	int x = 1;
	const int *p = &x;    // const pointer 
	*p = 2;    // An error is reported. This operation is not possible
	const int &x1 = x;    // const reference 
	x1 = 2;    //An error is reported. This operation is not possible
	return 0;
}

Pointer constant means that the pointer cannot be modified. In fact, it can be understood as an ordinary constant, but the value of this constant is an address value, which cannot be modified. In fact, the pointer cannot be modified. Examples are as follows πŸ‘‡

int main() {
	int x = 1, y = 2;
	int* const p = &x;    // constant pointer 
	p = &y;    // An error is reported. p is a constant. You can't do this
	return 0;
}

For reference constants, it means that the representative of the reference cannot be modified, that is, it cannot become the alias of other objects, which is actually a reference. A reference is a reference constant, and a reference constant is a reference.
three ️⃣ References are type safe, not pointers. There are more type checks for references than pointers.

✨ If you have any questions, please comment, leave a message or send a private letter below!

If this [article] is helpful to you πŸ˜„οΌŒ I hope I can give the blogger [some praise] πŸ‘] Encourage me 😘

❀️Thanks for your encouragement❀️

Topics: C++ Back-end