[C++: Quote]

To shine,not be illuminated.

Table of contents

1 Reference concept

2 Reference properties

3 Frequently cited

4 usage scenarios

 4.1 References as parameters

4.2 Do the return value

5 Efficiency comparison of passing by value and passing by reference

 6 Differences between references and pointers

7 summary


1 Reference concept

A reference is not a new definition of a variable, but an alias for an existing variable . The compiler will not open up memory space for the reference variable, and it shares the same memory space with the variable it refers to.

For example: your formal name and your nickname or alias.

Instructions:

类型& 引用变量名(对象名) = 引用实体;

We can give an example:

int main()
{
	int a = 10;
	int& sa = a;
	int b = 20;
	int& sb = b;
	return 0;
}

 Through debugging, we can also find that a and sa actually share the same space, which explains why the reference only gives the variable a new name, and does not open up a new space for the reference variable.

Note: Correct usage: the reference type must be of the same type as the referenced entity

2 reference features

  • 1. References must be initialized at definition time
  • 2. A variable can have multiple references
  • 3. Reference Once an entity is referenced, no other entity can be referenced

 Let's look at them one by one:

If the reference variable is not initialized

int main()
{
	int a = 10;
	int& sa ;
	
	return 0;
}

 This method will obviously report an error directly:

 A reference variable can have multiple references:

int main()
{
	int a = 10;
	int& b = a;
	int& c = a;
	
	return 0;
}

Here b and c are both aliases of a, modifying any of these three values ​​will change.

If you take an entity that has already been referenced and then refer to other entities:

 Writing in this way will directly report an error, and some people will think of writing it in another way:

int main()
{
	int a = 10;
	int& b = a;
	int e = 20;
	b = e;
	
	return 0;
}

Is b still a reference to e here?

Obviously not, here just assign the value of e to b, b is still a reference to a, after the program ends, both a and b should be 20, we can open the monitoring window to see:


3 often cited

Let's look at the following chestnut:

int main()
{
	const int a = 10;
	int& b = a;
	
	return 0;
}

Can the above code be compiled?

 Obviously, this way of writing is wrong. C++ thinks this way of writing is unsafe. const int has read-only permission, while int& has both read and write permissions. C++ does not support this kind of permission amplification. Minified or privileged equal C++ is supported.

For example, both of the following are legal:

int main()
{
	//权限相等
	const int a = 10;
	const int& b = a;
	//权限缩小
	int c = 20;
	const int& d = c;
	return 0;
}

Let's look at the following piece of code:

int main() 
{
	double d = 12.12;
	int& i = d;
	return 0;
}

Can the above code compile and pass?

 We may think: the reference type and the reference entity must be of the same type, so it is wrong.

Must the reference type and reference entity be of the same type?

Let's modify the program again:

 We found that the program can actually run, but the error becomes a warning, why?

Through the knowledge of C language, we know that if d of double type is converted into i of type int, it will be truncated. First, a temporary variable will be used to save the truncated value, and then the value will be replaced with a new name. d itself is unchanged. And this temporary variable is constant (this is very important and will be mentioned repeatedly later) and cannot be modified, that is, it cannot be written, but can only be read. Since the authority cannot be elevated, we must add const.

Let's answer the above question again, must the reference type and the reference entity be of the same type?

If you want to use it correctly, there is obviously nothing wrong with this sentence, but it does not mean that the reference type and the reference entity are not the same type and the program will definitely report an error.

Suggestion: If we refer to an entity in the future, if we do not want to change this entity, it is best to add const.


4 usage scenarios

The chestnuts cited above are basically not used in actual projects, and the cited usage scenarios are mainly the following two aspects:

  • 1. Make parameters
  • 2. Do the return value

 4.1 References as parameters

To give the simplest example, swap two variables, we used to do this:

void Swap(int* p1, int* p2)
{
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
}

Now it is easier to use references:

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

 These two functions constitute function overloading.

In addition, do you still remember the secondary pointer in the linked list? It made us miserable at the beginning. If you forget it, you can refer to this blog written by the blogger: Singly linked list

If you use references now, it will be awesome. Here, I only gave one of the interfaces, and the same is true for other interfaces:

void SLPushFront(SLNode*& phead, SLTDataType x)

{
	SLNode* newNode = SLCreat(x);
	newNode->next = phead;
	phead = newNode;
}

4.2 Do the return value

Let's see if there is any problem with the following program:

int& fun()
{
	int a = 10;
	return a;
}
int main()
{
	int ret = fun();
	cout << ret << endl;
	return 0;
}

Let's run the program:

 There seems to be no problem, but let's modify the program a little bit:

 Why did I add a cout to output a bunch of characters, but the result of my ret turned into a bunch of garbage numbers?

We know that a is a local variable, and it will be destroyed when it goes out of scope. Before we destroy it, we use a temporary reference variable to save the reference of a (assuming that the temporary variable is tmp (type is int&)), we find that tmp and ret are both aliases of the variable a, and a is released when it goes out of scope. When cout is not added to output a bunch of characters, we find that the result is correct only because a has not been changed after the space is released. When it is re-established for cout a has been modified in the stack frame, and it is extremely dangerous to return a reference to a local variable.

But if you modify it like this, you won't worry about this kind of problem:

int& fun()
{
	static int a = 10;
	return a;
}

Adding a static will not cause this problem.

Note: If the function returns out of the scope of the function, if the returned object has not been returned to the system, you can return by reference. If it has been returned to the system, you must return by value.
Let's look at the following question again:
int At(int i)
{
	static int a[10];
	return a[i];
}

int main()
{
	for (int i = 0; i < 10; i++)
	{
		At(i) = 10 + i;
	}

	for (int i = 0; i < 10; i++)
	{
		cout << At(i) << " ";
	}
	cout << endl;

	return 0;
}

Is there a problem with this program? Let's run it:

 The program reported an error, why?

Through the study of C language, we know that return by value is a temporary variable returned, which has constant properties (only readable, not writable), that is, it can only be used as an rvalue, not an lvalue, the above No. When a for loop writes to it, of course an error will be reported. The correct way to deal with it is to return it by reference:

 Why is it correct to return by reference? Since the reference is just an alias for a[i], a[i] has read and write functions, so no error will be reported.


5 Efficiency comparison of passing by value and passing by reference

Values ​​are used as parameters or return value types. During parameter passing and return, the function does not directly pass actual parameters or return the variable itself directly, but passes actual parameters or returns a temporary copy of the variable, so values ​​are used as parameters Or the return value type is very inefficient, especially when the parameter or return value type is very large, the efficiency is even lower.

 Let's test it out:

do parameter:

#include <time.h>
struct A { int a[100000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{
	A a;
	// 以值作为函数参数
	size_t begin1 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc1(a);
	size_t end1 = clock();
	// 以引用作为函数参数
	size_t begin2 = clock();
	for (size_t i = 0; i < 10000; ++i)
		TestFunc2(a);
	size_t end2 = clock();
	// 分别计算两个函数运行结束后的时间
	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

 do the return value:

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 is found that the efficiency of passing values ​​and pointers as parameters and return value types is very different .


 6 Differences between references and pointers

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

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

 Let's take a look at the assembly code comparison of references and pointers:

 Differences between references and pointers :

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

7 summary

This article mainly introduces the concept of references, how to use references and the usage scenarios of references, and also cites many error-prone chestnuts to help understand references, and finally lists the differences between references and pointers. If this article is helpful to your friends, can you support the selected blogger three times in a row?

Guess you like

Origin blog.csdn.net/m0_68872612/article/details/128398301