C Primer Plus (5th Edition)

2.5. References

A reference serves as an alternative name for an object. In real-world programs, references are primarily used as formal parameters to functions. We'll have more to say about reference parameters in Section 7.2.2 (p. 232). In this section we introduce and illustrate the use of references as independent objects.

A reference is a compound type that is defined by preceding a variable name by the & symbol. A compound type is a type that is defined in terms of another type. In the case of references, each reference type "refers to" some other type. We cannot define a reference to a reference type, but can make a reference to any other data type.

A reference must be initialized using an object of the same type as the reference:

int ival = 1024; int &refVal = ival; // ok: refVal refers to ival int &refVal2; // error: a reference must be initialized int &refVal3 = 10; // error: initializer must be an object

A Reference Is an Alias

Because a reference is just another name for the object to which it is bound, all operations on a reference are actually operations on the underlying object to which the reference is bound:

refVal += 2;

adds 2 to ival, the object referred to by refVal. Similarly,

int ii = refVal;

assigns to ii the value currently associated with ival.

When a reference is initialized, it remains bound to that object as long as the reference exists. There is no way to rebind a reference to a different object.

The important concept to understand is that a reference is just another name for an object. Effectively, we can access ival either through its actual name or through its alias, refVal. Assignment is just another operation, so that when we write

refVal = 5;

the effect is to change the value of ival to 5. A consequence of this rule is that you must initialize a reference when you define it; initialization is the only way to say to which object a reference refers.

Defining Multiple References

We can define multiple references in a single type definition. Each identifier that is a reference must be preceded by the & symbol:

int i = 1024, i2 = 2048; int &r = i, r2 = i2; // r is a reference, r2 is an int int i3 = 1024, &ri = i3; // defines one object, and one reference int &r3 = i3, &r4 = i2; // defines two references

const References

A const reference is a reference that may refer to a const object:

const int ival = 1024; const int &refVal = ival; // ok: both reference and object are const int &ref2 = ival; // error: non const reference to a const object

We can read from but not write to refVal. Thus, any assignment to refVal is illegal. This restriction should make sense: We cannot assign directly to ival and so it should not be possible to use refVal to change ival.

For the same reason, the initialization of ref2 by ival is an error: ref2 is a plain, nonconst reference and so could be used to change the value of the object to which ref2 refers. Assigning to ival through ref2 would result in changing the value of a const object. To prevent such changes, it is illegal to bind a plain reference to a const object.

Terminology: const Reference is a Reference to const

C++ programmers tend to be cavalier in their use of the term const reference. Strictly speaking, what is meant by "const reference" is "reference to const." Similarly, programmers use the term "nonconst reference" when speaking of reference to a nonconst type. This usage is so common that we will follow it in this book as well.

A const reference can be initialized to an object of a different type or to an rvalue (Section 2.3.1, p. 45), such as a literal constant:

int i = 42; // legal for const references only const int &r = 42; const int &r2 = r + i;

The same initializations are not legal for nonconst references. Rather, they result in compile-time errors. The reason is subtle and warrants an explanation.

This behavior is easiest to understand when we look at what happens when we bind a reference to an object of a different type. If we write

double dval = 3.14; const int &ri = dval;

the compiler transforms this code into something like this:

int temp = dval; // create temporary int from the double const int &ri = temp; // bind ri to that temporary

If ri were not const, then we could assign a new value to ri. Doing so would not change dval but would instead change temp. To the programmer expecting that assignments to ri would change dval, it would appear that the change did not work. Allowing only const references to be bound to values requiring temporaries avoids the problem entirely because a const reference is read-only.

A nonconst reference may be attached only to an object of the same type as the reference itself.

A const reference may be bound to an object of a different but related type or to an rvalue.

Exercises Section 2.5

Exercise 2.24:

Which of the following definitions, if any, are invalid? Why? How would you correct them?

(a) int ival = 1.01; (b) int &rval1 = 1.01; (c) int &rval2 = ival; (d) const int &rval3 = 1;

Exercise 2.25:

Given the preceeding definitions, which, if any, of the following assignments are invalid? If they are valid, explain what they do.

(a) rval2 = 3.14159; (b) rval2 = rval3; (c) ival = rval3; (d) rval3 = ival;

Exercise 2.26:

What are the differences among the definitions in (a) and the assignments in (b)? Which, if any, are illegal?

(a) int ival = 0; (b) ival = ri; const int &ri = 0; ri = ival;

Exercise 2.27:

What does the following code print?

int i, &ri = i; i = 5; ri =10; std::cout << i << " " << ri << std::endl;

Категории