Exploring C++ Series - References

foreword

First of all, you must know that a declaration statement consists of a basic data type (base type) followed by a list of declarators (declarator) .
Data types are easy to understand, such as int, double, char, string, etc., so what is this declarator list?
When we first learn C++, we need to define a variable, we use
the form of "data type + variable name", such as int a;
a is the name we give the variable. For a simple statement like this, the variable name is equal to the declarator list (of course, multiple variables can be defined in the form of int a,b,c;, which conforms to the concept of a list), but in fact, there can be more complex declarators
. Each declarator can name a variable and specify the type of the variable as a certain type related to the previously defined data type. This type is also called a composite type. The reference type in C++ is a composite type, so next, let's talk about what a reference is.


Introduction

References can also be understood as aliases for variables. The declarator is usually written in the form &x . x is the name we gave the variable. It is defined as follows

int num=1;
int &ref=num;

We define a variable of type int on the first line, and then assign it to a reference of type int on the second line. ref is the name of the reference, and then we add an & in front of it to represent that the type of the ref variable is a composite type based on the int type, and this ref becomes a reference of the int type.
Note: & is also part of the declarator . Why stress this point? Consider the following statement:

int i=1;
int &a=i,b;

May I ask: what type are a and b?
It's easy to understand if we remember that & is part of the declarator. This declaration statement defines two declarators: &a and b, and also defines the data type as int. There is an & in front of a, indicating that it is a reference of int type, which is a compound type, while b is just an ordinary int type.
Usually we connect & and variable names together, which is easy to understand, but this is not mandatory.

//以下两种定义引用的写法不报错
int i=1;
int& ref=i;
int & ref1=i;

But writing like int& a=i,b=i; may make people mistakenly think that a and b are both references. In fact, only a is a reference here , what we need to see is what the declarator is.

Going back to our first example

int num=1;
int &ref=num;

We try to output the value of ref

cout<<ref<<endl; 

The output result is 1, which is exactly the same as the value of num.
This is easy to understand, because we said before that references can be used as aliases for variables, just like when we give a person a nickname, we are actually calling this person by calling this nickname. But it should be noted that when defining a reference, the program binds the reference to its initial value instead of copying the initial value to the reference.
For example, int &ref=i; and int ref=i; The difference between them is that the latter will copy the initial value (the value of i), and then open up a new space in the memory for storing ref , while the reference does not have a separate memory space , It just refers to the assigned variable, giving the original variable another name, and operating on the reference is equivalent to operating on the original variable.
Let's demonstrate this with an example:

int i=5;
int &ref=i;
//输出地址
cout<<&i<<endl;
cout<<&ref<<endl;
int a=i;
cout<<&a<<endl;
//输出结果:
0x7fff59cca598
0x7fff59cca598
0x7fff59cca59c

It can be seen that the referenced memory address is the same as the original variable, and assigning a variable to another variable will open up a new memory space.
I don’t know if you have noticed here, why if I add & in the output statement, it will output a string of strange things (actually the memory address of the variable), isn’t the form of &x meant to be a reference?
In fact, & has different meanings in different situations (the same is true for * in C++ pointers). It can be used both as an operator in an expression and as part of a declaration . The & in the output statement in the above example represents the address-taking operator, and the address of the variable is obtained. In fact, friends who have learned C language should be more familiar with it. When I first started learning C++, I was relying on the old book of C language, so I would mistakenly think that the form of &x means to take the address. I didn't know that the reference and the address were taken until I learned the concept of "reference". Two different concepts, it seems that I was too young at that time haha.

The difference between referencing and addressing

Compared with C language, reference is a unique concept of C++.
When & appears immediately after the type name , such as int &ref=num, & is a part of the declaration at this time, which means reference.
When & appears in an expression , (the expression is a combination of one or more operands, and operators can be used to connect objects) such as &num, where & is a unary operator, which is an operator symbol for taking addresses .

Points to note when using references

1) The reference must be initialized at the time of declaration , and cannot be declared first and then assigned.
For example, it is wrong to write int &ref; alone, you must specify who is bound by the reference

2) Because the reference is bound to the variable (object) , (the meaning of the object and the variable in the follow-up is similar),
so assigning a value to the reference is actually assigning the value to the object bound to the reference. Getting the value of a reference actually gets the value of the object to which the reference is bound. Using a reference as the initial value is actually using the value of the object bound by the reference as the initial value.
Once the declaration of a reference is complete, it is not possible to rebind the reference to another object .
Let us prove the above with an example:

int num=1;
int &ref=num;
int &ref2=ref; //此时ref2的值是num的值,也就是1
ref2=3; //因为ref2也绑定到了num,此时就是修改num的值,num=3
int a=2;
ref=a;  //注意,这里相当于把a的值赋给num,并不是把ref绑定到a的意思
cout<<num<<endl; //此时num=2
cout<<ref<<" "<<ref2<<endl; //输出2 2 ,因为ref和ref2都是绑定在num上

3) Non-constant references can only be bound to objects, and cannot be bound to literal values ​​or the calculation result of an expression
. For example:

int a=1,b=2;
int &ref=2;  //错误的语法,2是一个字面值
int &ref=a*b;  //错误的语法,a*b是个表达式

In visual studio, writing like the above will report a " the initial value of a non-constant reference must be an lvalue " error.
There are lvalue and rvalue concepts in C++. We can simply understand that:
lvalues ​​have specific addresses in memory, such as created variables.
Rvalues ​​are generally non-addressable constants, or unnamed temporary objects created during expression evaluation, which are transient and
const-modified constant references, and there is no such restriction. I will write an article later on const for a more detailed introduction.

4) In most cases, the reference type must strictly match the bound object,
such as int i=1; double &d=i; an error will be reported because the type does not match

pass by reference

We often see that the parameter passed in is a reference type when defining a function, for example:

void swap(int &num1, int &num2){
    
    
	int temp=num1;
	num1=num2;
	num2=temp;
}
int main(){
    
    
	int a=1,b=2;
	swap(a,b);
	return 0;
}

This is a simple function that swaps values. What are the benefits of defining our formal parameters as reference types?
If & is not added, the parameters actually passed into the function will be passed to the formal parameters in the form of passing by value . When the function is executed, a space will be opened for the formal parameters on the stack, and the formal parameters receive only a copy of the actual parameter value .
(You can simply think that the formal parameters are the parameters defined in the parentheses when the function is defined, and the specific type is defined before the parameters; the actual parameters are the parameters passed in when the function is actually called) because in the scope of the function, the operation is the formal
parameter , so changing the value of the formal parameter will not affect the actual parameter. Thus, the function of value exchange cannot be realized.

But if the formal parameter is defined as a reference, we know that the variable itself is the name of a memory address that holds data, and the reference is another name of the variable. When passing a reference, the formal parameters of the function will still open up memory space on the stack as a local variable, but because we pass in an alias, the alias refers to the name of the corresponding variable, which is equivalent to the memory address of the data . Therefore, the newly opened space of the function stores the memory address of the actual parameter. If we modify the data of the formal parameter, the data in the memory space of the actual parameter will also be modified.

The difference between references and pointers

A pointer "points" to an object in memory, and a reference is "bound to" an object in memory. They both implement indirect access to other objects. The difference between the two is mainly in two aspects:

1) The pointer itself is an object, allowing assignment and copying of the pointer, and it can point to several different objects during the declaration cycle of the pointer; the reference is not an object, and the reference cannot be rebound to another object.

2) The pointer does not need to be assigned an initial value at the time of definition. Like other built-in types, if the pointer defined in the block scope is not initialized, it will also have an indeterminate value; the reference must be assigned an initial value at the time of definition.


Written at the end:
This article is some notes I made when I was learning C++. There may be some more in-depth points that have not been learned. If there are supplements, they will be updated later.

Guess you like

Origin blog.csdn.net/qq_46044366/article/details/119280490