Understanding the differences between reference and pointer can help you decide when to use reference and when to use pointer.
In C ++, reference has the same capabilities as pointer in many aspects. Although most C ++ programmers have some intuition about when to use the reference and when to use the pointer, they may still be confused sometimes. If you want to establish a clear and rational concept for using reference, it is also necessary to understand the differences between reference and pointer.
Deep Meaning
Similar to pointer, a reference is an object that can be used to indirectly point to another object. The declaration of a reference is the same as the substantive syntax structure of the declaration of pointer. The difference is that the asterisk operator * is used when the pointer is declared, and the address operator * is used when the reference is declared &. For example, we have:
Int I = 3;
There are:
Int * pi = & I;
Declare pi as a pointer type object, and it is a "pointer to an int integer", its initial value is the address of object I. On the other hand:
Int & ri = I;
Declare ri as a reference object, and it is also a reference pointing to an integer. It points to I. We can see that the declaration of pointer and reference is significantly different, but this is not the basis for deciding which one to use. The true basis of the decision is that when they are used in expressions, their explicit differences determine which one is suitable for use.
The biggest difference between Pointer and reference is that pointer must use an asterisk operator * to remove reference (the English name is dereference. I don't know how to translate this word here, let's just call it "reference") and reference does not require any operators for reference. For example, with the definition in the above example, the indirect expression * pi references pi as pointing to I. Conversely, expression ri-No operator is required-the ri is automatically referred to as pointing to I. Therefore, to use the pointer p, we need to use the value assignment statement:
* P = 4;
Change the value of I to 4, and use reference ri, we only need to directly write:
Ri = 4;
You can also change the value of I to 4.
This display is different when you choose whether to use pointer or reference for the parameter type and Return Value Type of the function, especially for functions with overloaded operators.
The following uses a ++ operator for enumeration type (enumeration) to illustrate this point. In C ++, the built-in ++ operator is invalid for the enumerated type. For example, it is defined as follows:
Enum day {
Sunday, Monday ,...
};
Day x;
Expressions ++ x cannot be compiled. To compile a function, you must define a function named operator ++, accept day as the parameter, and call ++ x to change the value of x. Therefore, declare only one function operator ++, with the Type day as the parameter, as follows:
Day operator ++ (day d );
And cannot get the desired effect. This function passes the parameter (pass by value) through the value, which means that a copy of the parameter is seen in the function, rather than the parameter itself. In order for a function to change its operand value, it must pass its operand through a pointer or reference.
The passing by pointer function is defined as follows:
Day * operator ++ (day * d );
It stores the added value in * d to change the date (day) value of the function. However, you must use the expression ++ & x to call this operator.
The correct method is to define operator ++ with reference as the parameter type, as follows:
Day & operator ++ (day & d)
{
D = (day) (d + 1 );
Return d;
}
Using this function, the expression ++ x is correctly displayed and operated.
Passing by reference is not only a good method for writing operator ++, but also a unique method. C ++ has no choice here. As declared below:
Day * operator ++ (day * d );
It cannot be compiled. Each overloaded operator function must be a member of a class, or use the type T, T &, or T const & as the parameter type. Here T is a class or enumeration) type. That is to say, each overload operator must take the class or enumeration type as the parameter type. A pointer, even a pointer to a class or an object of the enumerated type, cannot be used. C ++ does not allow you to redefine the meaning of the built-in operators, including the pointer type, when overloading operators. Therefore, we cannot define:
Int operator ++ (int I); // Error
Because it tries to redefine the meaning of the operator ++ for int. We cannot define either:
Int * operator ++ (int * I); // Error
Because it tries to redefine the meaning of the operator ++ for int.
References vs. const pointers
"Const reference" cannot be defined in C ++, because a reference is inherently const. That is to say, once a reference is bound to an object, it cannot be re-bound to another object. After a reference is declared, it cannot be re-bound to another object. For example:
Int & ri = I;
Bind the ri to I. Then, assign the following values:
Ri = j;
Instead of binding the ri to j, the value in j is assigned to the object pointed to by ri, that is, to I.
In short, a pointer can point to many different objects in its lifetime, and a reference can point to only one object. Some people think that this is the biggest difference between reference and pointer. I disagree. Maybe this is the difference between reference and pointer, but it is not the difference between reference and const pointer. Once a reference is bound to an object, it cannot be changed to another object. Since it cannot be changed after the reference is bound, a reference must be bound at birth. Otherwise, this reference will never be bound to anything, and it will be useless.
The previous discussion also applies to the const pointer ). (Note: Here I am talking about the const pointer instead of the constant pointer "pointers to const ".) For example, a reference declaration must carry an initialization value at the same time, as shown below:
Void f ()
{
Int & r = I;
...
}
Omitting this initialization value will generate a compilation error:
Void f ()
{
Int & r; // Error
...
}
The declaration of a constant pointer must also carry an initialization value, as shown below:
Void f ()
{
Int * const p = & I;
...
}
If this initialization value is omitted, the following error occurs:
Void f (){
Int * const p; // Error
...
}
In my opinion, it is impossible to bind a reference as a reference instead of a pointer. It is not more significant than the difference between a constant pointer and a constant pointer.
Null references
In addition to the differences in display, constant pointers and reference are also very different, that is, a valid reference must point to an object, and a pointer does not need. A pointer, even a constant pointer, can have null values. A null pointer does not point to anything.
This difference implies that when you want to make sure that a parameter must point to an object, you should use reference as the parameter type. For example, the swap function accepts two int parameters, and the values of the two parameters are reversed, as shown below:
Int I, j;
Swap (I, j );
Put the value originally in I into j and put the value originally in j into I. We can write this function as follows:
Void swap (int * v1, int * v2)
{
Int temp = * v1;
* V1 = * v2;
* V2 = temp;
}
In this definition, the function is called like this: swap (& I, & j );
This interface implies that either of the two parameters may be null ). This suggestion is misleading. For example
Swap (& I, NULL );
The consequences may be unpleasant.
Reference is defined as a parameter as follows:
Void swap (int & v1, int & v2)
{
Int temp = v1;
V1 = v2;
V2 = temp;
}
Clearly indicates that two objects should be provided to call swap, and their values will be exchanged. Another benefit of this definition is that when calling this function, you do not need to use the & symbol, which looks more pleasing to the eye:
Swap (I, j );
More secure?
Some people think that since the reference cannot be null, it should be safer than the pointer. I think the reference may be a little safer, but not much safer. Although a valid reference cannot be blank, it is not valid. In fact, in many cases, the program can generate invalid reference, not just empty reference. For example, you can define a reference to bind it to an object pointed to by a pointer, as shown below:
Int * p;
...
Int & r = * p;
If the pointer * p is null when the reference is defined, the reference is null. Technically, this error does not mean binding a reference to a null value, but rather a reference to a null pointer. An uncertain operation is generated for a null pointer reference, which means that many things may happen, and most of them are not good. It is very likely that when the program binds the reference r to * p (the object pointed to by p), p is not actually referenced, even the program just copies the p value to the r pointer. The program will continue to execute until the error is displayed in a more obvious table in the subsequent operation, causing unpredictable harm.
The following function shows another method for generating invalid reference:
Int & f ()
{
Int I;
...
Return I;
}
This function returns a reference pointing to the local variable I. However, when the function returns, the storage space of the local variable I disappears. Therefore, this function returns a reference pointing to the space to be recycled. This operation has the same consequence as returning a pointer to a local variable. Some compilers may find this error during compilation, but they may not.
I like reference and have good reasons to use them instead of pointer. However, if you want to use reference to significantly improve the robustness of your program, you will probably be disappointed.
References:
Saks, Dan. "Introduction to References," Embedded Systems Programming, January 2001, p. 81.
Saks, Dan. "References and const", Embedded Systems Programming February 2001, p. 73.