[C++] C++ citation (Can’t quote? Solve the many details of citation in detail!)

C++ reference directory:

1. The concept of citation

2. Characteristics of citations

2.1 References must be initialized when defined

2.2 A variable can have multiple references

2.3 Once a reference refers to an entity, it cannot refer to other entities.

3. Constant references (references with const)

3.1 Temporary variables are permanent and cannot be modified (return by value, generated during implicit/forced type conversion)

3.2 In assignment, the permissions of pointers/references can be reduced, but cannot be enlarged.

3.3 Constant references as parameters

3.4 How to reference default parameters?

4. Reference usage scenarios

4.1 Make parameters (reduce copying and improve efficiency, integrate real and physical shapes)

4.2 Make return values ​​(reduce copies to improve efficiency, modify return values)

4.2.1 What does memory space destruction mean? & What happens when accessing destroyed memory space?

4.2.2 Using references as return values ​​improves efficiency

4.2.3 References used as return values ​​can be modified

5. The difference between references and pointers


Tips: View assembly code with vscode:

In the location of the debug controller enter:

-exec disassemble /m

1. The concept of citation

The basic form is: type & reference variable name (object name) = reference entity

A reference is to give an alias to an existing variable : a reference is not to newly define a variable, but to give an alias to an existing variable. The compiler will not open up memory space for the reference variable. It shares the same memory with the variable it refers to. space

The reference symbol is the same as the symbol for taking addresses in our C language, which is & ; adding the reference symbol (&) after a certain type name becomes a reference type . The purpose of designing references is to simplify the use of pointers, but references cannot replace pointers (in fact, the bottom layer of references is implemented using pointers)

The difficulty is that the symbols are the same as the address symbols. For example, when you see *&, you will be confused. And you must remember that what follows the type is not an address, but an alias.

void TestRef()
{
	int a = 10;
	int& ra = a;//<====定义引用类型
	printf("%p\n", &a);
	printf("%p\n", &ra);
}

a and ra both point to this space

Note: The reference type must be of the same type as the referenced entity (the reason for this will be explained in the enlargement and reduction of permissions for frequent references)


2. Characteristics of citations

2.1 References must be initialized when defined

Note: const int&b=10; this can also be passed, and this is also considered initialization (as for why const is added, please see the commonly quoted knowledge below)

2.2 A variable can have multiple references

int a = 10;
int& b = a;
int& c = a;
int& d = a;

At this time, b, c, and d are all references to variable a.

2.3 Once a reference refers to an entity, it cannot refer to other entities.

The meaning of this sentence also indirectly shows that C++ references cannot replace pointers, because pointers can change the link address.

(In Java and Python, references can be changed)

	int a = 10;
	int& b = a;

At this time, b is already a reference to a, and b can no longer refer to other entities. If you write the following code, you want b to refer to another variable c:

	int a = 10;
	int& b = a;
	int c = 20;
	b = c;//你的想法:让b转而引用c

But the code does not follow your wishes. The meaning of the code is: assign the entity referenced by b to c, that is, change the content of variable a to 20


3. Constant references (references with const)

A constant reference is to modify the reference type with const; just like the const modified variable, the reference modified by cosnt can only be read, but not written, that is, the value of the reference variable cannot be modified.

3.1 Temporary variables are permanent and cannot be modified (return by value, generated during implicit/forced type conversion)

Temporary variables in C++ refer to variables without names that are generated on the stack by the compiler as needed. There are two main types of uses. Temporary variables are permanent.

1) The return value of the function

This picture is very important and must be seen:

 Let’s give another example of receiving without reference:

In C++, if a function returns a temporary variable (returned by value), then the temporary variable is an rvalue and cannot be modified. `Add(a,b)` returns a temporary variable, so it cannot be assigned directly . If you want to modify the result, you can store it in a variable and then operate on the variable.

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

int main() {
    int a = 0;
    int b = 3;
    int result = Add(a, b);
    result += 1;
    return 0;
}

I still have a small question, why can const type be passed to int type? ? (Simply assign permissions without talking about enlarging or reducing them)

Only references and pointers need to be considered, everything else is fine, so here is the simplest one:

int gives error to const (because const can only be initialized when defined)

const to int pair (you can assign a const type value to a non-const type variable, but you cannot assign a non-const type value to a const type variable, because this will destroy the read-only nature of const)

But when assigning values ​​by reference below, be careful of confusion!

int& a=10 This is const giving int, which is wrong to enlarge the permissions (originally const is read-only, but int devils invaded, of course it won't work)

int b=2;const int& c=b; This is int given to const. It is reasonable to reduce the permissions (I used to be careless, but then my wife controlled me strictly)

2) Intermediate variables during type conversion

int main()
{
	int d=10;

	int i=(int)d;//强制类型转换,并不是改变了变量d,而是产生临时变量
	int i=d;//隐式类型转换,也是产生了临时变量

	double d=12.34;
	const int& ri=d;//这里引用的实体其实就是从double d 到int类型转换中间的临时变量
	return 0;
}

After understanding this picture thoroughly, the problem of const temporary variables can come to an end. End of digression, let’s get to the main topic

Of course, this code will not run because there are a lot of redefinitions

3.2 In assignment, the permissions of pointers/references can be reduced, but cannot be enlarged.

int main()
{
	const int a = 10;
	//int& ra = a;    //该语句编译时会出错,a为常量,权限放大
	const int& ra = a;//正确,权限平移
	
	//int& b = 10;    //该语句编译时会出错,10为常量,权限的放大
	const int& b = 10;//正确,权限平移

    int c=10;
    const int& rc=c;  //正确,权限缩小
	return 0;
}

Permissions can only be reduced and panned, not enlarged

Note: The permissions here refer to read and write permissions, and only apply to pointers and references.

3.3 Constant references as parameters

a. Generally, constant references are used as parameters , that is, const+references. If const is not used, there may be a problem of permission amplification, and constant references can receive both read-only permissions and readable and writable permissions.

b. Constant references are not used to modify parameters, but to reduce copying and improve efficiency.

Of course, not all parameters can be added with const. For example, if we add const to our swap function, it will be counterproductive.

3.4 How to reference default parameters?

If you want to use the default parameter as a reference, you must use a constant reference, because the default parameter is a constant and is not allowed to be modified. It can only be read.

void func(const int& N = 10)
{

}

4. Reference usage scenarios

4.1 Make parameters (reduce copying and improve efficiency, integrate real and physical shapes)

When calling a function, the formal parameters must be copied in the function stack frame where they are located, so if you call by value, a temporary copy of the actual parameters will be made when calling the function. If you are Calls by address and pointer variables also need to open up their own space, so these are consumption of program performance.

References can directly change actual parameters. As input parameters and output parameters, pointers no longer need to be passed; for example, in the Swap function below, what we exchange inside the Swap function is actually the values ​​of the two actual parameters, and there is no need to pass them as before. pointer to actual parameters

But if we use references as parameters, we will not have these problems, because the operating system does not allocate a separate space for reference variables, and reference variables do not need to copy actual parameters, which can reduce copying and improve efficiency.
And since the reference is essentially the actual parameter itself, it can also be used as an output parameter to modify the actual parameters outside the function.

//单链表
typedef int SLTDataType;  //数据类型重命名
typedef struct SListNode   //链表的一个节点
{
	SLTDataType data;
	struct SListNode* next;  //存放下一个节点的地址
}SLTNode;

//在头部插入数据
void SListPushFront(SLTNode*& rphead, SLTDataType x)  //引用做形参,直接操作plist
{
	SLTNode* newNode = BuySLTNode(x);  //开辟新节点
	newNode->next = *rphead;
	*rphead = newNode;
}

In operations such as inserting the head of a singly linked list, since the singly linked list we implemented before does not have a head, we need to pass the pointer of plist to facilitate changing the plist when inserting the first data. Now, we can directly use the reference of plsit. However, there is no need to pass a secondary pointer, making the code easier to understand. Of course, we cannot design the next pointer in SListNode as a reference, because the next of the tail node is constantly changing, and once a reference refers to an entity, it cannot refer to other entities. This also shows that references cannot be replaced. Pointers can only simplify pointers

4.2 Make return values ​​(reduce copies to improve efficiency, modify return values)

Let’s first discuss the issue of local variable space destruction

4.2.1 What does memory space destruction mean? & What happens when accessing destroyed memory space?

Destroying the memory space does not mean tearing up the memory space. It permanently destroys this space. The memory space will not disappear. It will remain there intact. However, after the memory space is destroyed, its The right to use it no longer belongs to us. The data we saved in it is no longer protected and may have changed.

After destruction, we can still access this space, but the access behavior is uncertain, and our behavior of reading and writing data in the space is unpredictable.

Treat the space as a hotel and the data as apples:
a. The apple is not lost b. The room is not lost, but it becomes a random fruit c. The apple is covered as a fixed fruit

Example one:

int& Func()
{
	static int n = 0;
	n++;
	printf("&n:%p\n", &n);
	return n;
}

int main()
{
	int ret = Func();
	cout << ret << endl;
	printf("&ret:%p\n", &ret);

	int& rret = Func();
	printf("&rret:%p\n", &rret);
	return 0;
}

The address of n is the same as rret, which means that the reference returns the variable n itself, not just the value of n.

In the Func function, n is a static variable, and static variables open up space in the static area and not in the stack area, so n will not be destroyed when the stack frame of the Func function is destroyed ;

At the same time, when we use a reference as the return value, it is equivalent to directly returning the variable n to the function caller. Therefore, when we use a reference to receive it, it is equivalent to giving n another alias;

Example 2: (Note: Some of the following ways of using quotes are incorrect demonstrations, so you must follow the blogger to understand them slowly)

Analysis: For the code on the left , after leaving the Count function stack frame, the variable n will be destroyed. The returned alias points to a destroyed variable space name. If you use the returned alias at this time, the results will be unpredictable. (The apple example cited above), since the n variable is destroyed, it must not be returned by reference (but there will be no syntax compilation errors, so be careful)

For the code on the right , the variable n will not be destroyed and is stored in the static area, so it can be returned by reference.

Analysis: The functions have been destroyed, why can the return value still be brought out? This is because before executing the return statement, the system will automatically create a temporary variable temp. The storage address of this variable is allocated according to the size of the return content. It may be a register or a heap. We cannot predict it. Then the operating system will The value of the variable to be released is assigned to this temporary variable, and the return value is brought out through the temporary variable.

#include<iostream>
using namespace std;

int& Count()
{
	int n = 0;//n存放在栈区
	n++;
	//...
	return n;
}
void Func()
{
	int x=100;
}
int main()
{
	int &ret = Count();
	cout << ret << endl;

	cout << ret << endl;

	Func();
	cout << ret << endl;
	return 0;
}

analyze:

1. The n variable in the Func function is a local variable and is destroyed after the function call is completed. However, we use a reference as the return value here, so the stack space of n is returned;

2. ret is a reference to the return value of the Func function, and the function return value is a reference to the local variable n, so the first time we print is the value of n, 1;

3. We called the printf function when printing ret. This function used the space where Count was destroyed, so the original data in the n space was overwritten, and ret is a reference to n, so printing ret means printing the original data in the printf function. The data in the memory space of n, so what is printed is a random value.

4. We called the Func function, and then found that the address of the x variable in Func is the same as the original address of n, which means that the Cover function uses the space of the printf function again, and the space of x happens to be located at the previous position of n, so the print ret The result is 100;

After the problem of space destruction is solved, continue to give an error code that refers to the return value, and continue to understand and analyze:

4.2.2 Using references as return values ​​improves efficiency

According to the above return by value, it is known that temporary variables will be created, so it is obvious that return by reference improves efficiency.

4.2.3 References used as return values ​​can be modified

The following code demonstrates the use of references as return values ​​in C++ to modify the return value .
Expand all even numbers in the array by twice

int& change(int* arr,int i)
{
	return arr[i];
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	for (int i = 0; i < 10; i++)
	{
		if (arr[i] % 2 == 0)
		{
			change(arr, i) *= 2;
		}
	}
	for (int i = 0; i < 10; i++)
	{
		cout << arr[i] << " ";
	}
}

Summarize:

The return variable does not exist after leaving the function scope, and it cannot be returned by reference because the result returned by the reference is undefined. The
return variable outside the function scope exists and can only be returned by reference.


5. The difference between references and pointers

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

In terms of underlying implementation: the reference actually has space, because the reference is implemented in the form of a pointer.

When we debug the code and then turn to the disassembly, we can find that the assembly code of the reference and the pointer are exactly the same, that is, the bottom layer of the reference is actually the pointer.

The difference between references and pointers (9 major points)

  • A reference conceptually defines an alias of a variable, and a pointer stores a variable address;
  • References must be initialized when defined , but pointers are not required;
  • After a reference refers to an entity during initialization, it can no longer refer to other entities, while a pointer can point to any entity of the same type at any time;
  • There is no NULL reference, but there is a NULL pointer;
  • 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 (4 bytes on a 32-bit platform);
  • 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 ;
  • There are multi-level pointers, but there are no multi-level references;
  • The way to access entities is different, the pointer needs to be explicitly dereferenced, and the reference compiler handles the underlying processing by itself;
  • References are relatively safer to use than pointers.

I hope this painstaking full text of C++ quotation can be helpful to you! ! ! come on! !

Guess you like

Origin blog.csdn.net/weixin_62985813/article/details/132724155