c++ 引用(reference)和指针 (pointer)
Reference
A reference defines an alternative name for an object.
A reference type “refers to” another type. We define a reference type by writing a declarator of the form &d, where d is the name being declared:
int ival = 1024;
int &refVal = ival; // refVal refers to (is another name for) ival
int &refVal2; // error: a reference must be initialized
Ordinarily, when we initialize a variable, the value of the initializer is copied into the object we are creating.
When we define a reference, instead of copying the initializer’s
value, we bind the reference to its initializer.
Once initialized, a reference remains bound to its initial object.
There is no way to rebind a reference to refer to a different
object.
Because there is no way to rebind a reference, references must be initialized.
Note:
A reference is not an object. Instead, a reference is just another name for an already existing object.
After a reference has been defined, all operations on that reference are actually operations on the object to which the reference is bound.
Because references are not objects, we may not define a reference to a reference.
Rules:
- the type of a reference and the object to which the reference refers must match exactly.
There are two exceptions to the rule that the type of a reference must match the type of the object to which it refers:
References to const
- a reference may be bound only to an object, not to a literal or to the result of a more general expression:
int &refVal4 = 10; // error: initializer must be an object
double dval = 3.14;
int &refVal5 = dval; // error: initializer must be an int object
Pointer
A pointer is a compound type that “points to” another type.
Like references, pointers are used for indirect access to other objects.
Unlike a reference, a pointer is an object in its own right.
Pointers can be assigned and copied; a single pointer can point to
several different objects over its lifetime.
Unlike a reference, a pointer need not be initialized at the time it is defined.
Like other built-in types, pointers defined at block
scope have undefined value if they are not initialized.
Note:
Because references are not objects, they don’t have addresses. Hence, we may not define a pointer to a reference.
Null Pointers
A null pointer does not point to any object. Code can check whether a pointer is null before attempting to use it.
There are several ways to obtain a null pointer:
int *p1 = nullptr; // equivalent to int *p1 = 0;
int *p2 = 0; // directly initializes p2 from the literal constant 0
// must #include cstdlib
int *p3 = NULL; // equivalent to int *p3 = 0;
The most direct approach is to initialize the pointer using the literal nullptr, which was introduced by the new standard.
nullptr is a literal that has a special type that can be converted to any other pointer type.
Alternatively, we can initialize a pointer to the literal 0, as we do in the definition of p2.
Older programs sometimes use a preprocessor variable named NULL, which the cstdlib header defines as 0.
The preprocessor is a program that runs before the compiler.
Preprocessor variables are managed by the preprocessor, and are not part of the std namespace. As a result, we refer to them directly without the std:: prefix.
When we use a preprocessor variable, the preprocessor automatically replaces the variable by its value.
Hence, initializing a pointer to NULL is equivalent to initializing it
to 0.
Modern C++ programs generally should avoid using NULL and use nullptr instead.
It is illegal to assign an int variable to a pointer, even if the variable’s value happens to be 0.
int zero = 0;
pi = zero; // error: cannot assign an int to a pointer
Advice: Initialize all Pointers
If there is no object to bind to a pointer, then initialize the pointer to nullptr or zero. That way, the program can detect that the pointer does not point to an object.
void* Pointers
The type void* is a special pointer type that can hold the address of any object.
Like any other pointer, a void* pointer holds an address, but the type of the object at that address is unknown:
Rules:
- We can compare it to another pointer.
- We can pass it to or return it from a function.
- We can assign it to another void* pointer.
- We cannot use a void* to operate on the object it addresses—we don’t know that object’s type, and the type determines what operations we can perform on the object.
Generally, we use a void* pointer to deal with memory as memory, rather than using the pointer to access the object stored in that memory.
References to Pointers
A reference is not an object. Hence, we may not have a pointer to a reference.
However, because a pointer is an object, we can define a reference to a pointer:
int i = 42;
int *p; // p is a pointer to int
int *&r = p; // r is a reference to the pointer p
r = &i; // r refers to a pointer; assigning &i to r makes p point to i
*r = 0; // dereferencing r yields i, the object to which p points; changes i to 0
The easiest way to understand the type of r is to read the definition right to left.
The symbol closest to the name of the variable (in this case the & in &r) is the one that has the most immediate effect on the variable’s type.
Thus, we know that r is a reference. The rest of the declarator determines the type to which r refers.
The next symbol, * in this case, says that the type r refers to is a pointer type.
Finally, the base type of the declaration says that r is a reference to a pointer to an int.