Constant references and temporary variables

1. Citation

Reference is a feature introduced in C++ that surpasses C. In C, parameters are passed by value when a function is called, so that the modification of parameters in the called function cannot affect the original function. In addition, due to copying The overhead of deep recursion and variable transfer that occupies a large memory can easily explode the stack ->__->. After adding a reference, pass by reference, so that the function is no longer a copy of the actual parameter. example:

#include <iostream>
intmain()
{
	using namespace std;
	int rats = 101;
	int & rodents = rats; // Note that references are not disguised representations of pointers, in fact, references must be initialized when they are declared, not declared and then initialized
	cout << "rats = " << rats << endl;
	cout << "rodents = " << rodents << endl;
	rodents++;
	cout << "rats = " << rats << endl;
	cout << "rodents = " << rodents << endl;
	cout << "The address of rats: " << &rats << endl;
	cout << "The address of rodents: " << &rodents << endl;
	while (cin.get() != 'q')
		;
	return 0;
}

The result of running the above code is:

rats = 101

rodents = 101

rats = 102

rodents = 102

The address of rats: 006FFD4C

The address of rodents: 006FFD4C

It can be seen that rodents is actually an alias of the variable rats, with the same address, and modifications to rodents will also be reflected on rats.

2. Temporary variables and constant references

It should be pointed out that pass-by-reference has greater restrictions on the matching degree of actual parameters and formal parameters. First, let's take a look at the reaction of the two function declarations of const reference and reference parameter in the face of different types of actual parameters. :

#include <iostream>
using namespace std;
void test_const(const int &a)
{
	cout << "Yes, you can do it!" << endl;
}
void test(int &a)
{
	cout << "Yes, you can do it!" << endl;
}
intmain()
{
	int a = 3;
	double b = 3;
	short c = 3;
	test_const(a);	    //	valid
	test_const(b);	    //	valid
	test_const(c);	    //	valid
	test_const(a + 1);  //	valid
	test(a);	    //	valid
	test(b); // invalid double type cannot be converted to int &
	test(c); // invalid short type cannot be converted to int &
	test(a + 1);	    //  invalid
	while (cin.get() != 'q')
             ;
        return 0;
}

Note that for ordinary reference parameters, the parameter matching requirements are very high. For the case where the parameter is a const reference, C++ will generate a temporary variable when the actual parameter and the reference parameter do not match:

a) the argument has the correct type, but is not an lvalue

b) the type of the actual parameter is incorrect, but can be converted to the correct type

Let's discuss the definition of lvalue first:

When I was learning C language, I actually always understood that lvalue is the value that can be assigned, that is, the value that can be placed on the left side of '='. In C language, this sentence should be correct, but in C++, lvalue also includes , const variable.

a) The lvalue parameter is a data type that can be referenced , eg: variable x, array element a[0], structure member student.name, pointer p, *p

b) Typical non-lvalues, literal constants (444), expressions containing multiple terms (x+1-9), note that "abc" is an lvalue because its value is actually an address

c) Regular variables are modifiable lvalues, and const variables are unmodifiable lvalues. The common point is that they can all be accessed through addresses.


#include <iostream>
using namespace std;
double refun(const double &a)
{
	return a * a * a;
}

intmain()
{
	double side = 3.0;
	double * pd = &side;
	double & rd = side;
	long edge = 5L;
	double len[4] = { 2.0,5.0,10.0,12.0 };
	double c1 = refun(side); // lvalue && type match
	double c2 = refun(len[2]); // lvalue && type match
	double c3 = refun(rd); // lvalue && type match
	double c4 = refun(*pd); // lvalue && type match
	
	double c5 = refun(edge); // lvalue, but type mismatch
	double c6 = refun(7.0); // the type is correct, but not an lvalue
	double c7 = refun(side + 10.0); // the type is correct, but not an lvalue

	cout << "c1 = " << c1 << endl;
	cout << "c2 = " << c2 << endl;
	cout << "c3 = " << c3 << endl;
	cout << "c4 = " << c4 << endl;
	cout << "c5 = " << c5 << endl;
	cout << "c6 = " << c6 << endl;
	cout << "c7 = " << c7 << endl;
	while (cin.get() != 'q')
		;
	return 0;
}

For the three calls of c5, c6, and c7, the compiler will generate a temporary anonymous variable and let a point to it. These temporary variables only exist during the function call cycle, and the compiler can delete them at will.

The function I originally wanted to write was a = a + 1; because I wanted to see if the reference caused by the temporary variable would modify the original variable, and later found that the const variable could not be modified, so what is the use of this constant reference...

I compared:

a) Passing by value can protect data (without affecting actual parameters), but it will generate copy overhead, especially when large-capacity parameters are used.

b) Ordinary reference passing: It can save the cost of copying and can affect the actual parameters, but the problem is that the variable restrictions are very strict when calling

c) Constant reference: When we do not want to modify the actual parameters, but also want to use a reference, the constant reference puts more restrictions on variables, and at the same time saves the cost of copying.



In the last program, both swaps fail to compile, one is because the types do not match, and the other is because you have modified the const variable

#include <iostream>
void swap(int &a, int &b)
{
	int temp;

	temp = a;
	a = b;
	b = temp;
}

void swapcon(const int &a, const int &b)
{
	int temp;
	temp = a;
	a = b;
	b = temp;
}

intmain()
{
	long a = 3, b = 5;
	swap(a, b);
	swapcon(a, b);
	while (cin.get() != 'q')
		;
	return 0;
}

Reasons to use const:

a) Avoid the error of unintentionally modifying the data. As you can see from the last program, the compiler will help you catch this kind of error. The most terrible thing is that it has been compiled and run, and the result is wrong. . .

b) const enables the function to handle const and non-const arguments, otherwise it can only accept non-const arguments: (can't assign a const variable to a non-const pointer/reference)

c) Using const references enables functions to correctly generate and use temporary variables


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324729126&siteId=291194637