References
in C++
A
reference
(&)
is like a constant pointer that is automatically dereferenced. It is usually
used for function argument lists and
function return values.
But you can also make a free-standing reference. For example,
int x;
int & r = x;
When
a reference is created, it must be initialized to a live object. However, you
can also say
Here,
the compiler allocates a piece of storage, initializes it with the value 12,
and ties the reference to that piece of storage. The point is that any
reference must be tied to someone
else’s
piece of storage. When you access a reference, you’re accessing that
storage. Thus if you say,
int x = 0;
int & a = x;
a++;
incrementing
a
is actually incrementing
x.
Again, the easiest way to think about a reference is as a fancy pointer. One
advantage of this pointer is you never have to wonder whether it’s been
initialized (the compiler enforces it) and how to dereference it (the compiler
does it).
There
are certain rules when using references:
- A
reference must be initialized when it is created. (Pointers can be initialized
at any time.)
- Once
a reference is initialized to an object, it cannot be changed to refer to
another object. (Pointers can be pointed to another object at any time.)
- You
cannot have NULL reference
s.
You must always be able to assume that a reference is connected to a legitimate
piece of storage.
References
in functions
The
most common place you’ll see references is in function arguments and
return values. When a reference is used as a function argument,
any modification to the reference
inside
the function will cause changes to the argument
outside
the function. Of course, you could do the same thing by passing a pointer, but
a reference has much cleaner syntax. (You can think of a reference as nothing
more than a syntax convenience, if you want.)
If
you return a
reference from a function, you must take the same care as if you return a
pointer from a function. Whatever the reference is connected to shouldn’t
go away when the function returns; otherwise you’ll be referring to
unknown memory.
//: C11:Reference.cpp
// Simple C++ references
int* f(int* x) {
(*x)++;
return x; // Safe; x is outside this scope
}
int& g(int& x) {
x++; // Same effect as in f()
return x; // Safe; outside this scope
}
int& h() {
int q;
//! return q; // Error
static int x;
return x; // Safe; x lives outside scope
}
int main() {
int a = 0;
f(&a); // Ugly (but explicit)
g(a); // Clean (but hidden)
} ///:~
The
call to
f( )
doesn’t have the convenience and cleanliness of using references, but
it’s clear that an address is being passed. In the call to
g( ),
an address is being passed (via a reference), but you don’t see it.
const
references
The
reference argument in
Reference.cpp
works only when the argument is a non-
const
object. If it is a
const
object, the function
g( )
will not accept the argument, which is actually a good thing, because the
function
does
modify the outside argument. If you know the function will respect the
constness
of an object, making the argument a
const
reference will allow the function to be used in all situations. This means
that, for built-in types, the function will not modify the argument, and for
user-defined types the function will call only
const
member functions, and won’t modify any
public
data members.
The
use of
const
references in function arguments is especially important because your function
may receive a temporary object,
created as a return value of another function or explicitly by the user of your
function. Temporary objects are always
const,
so if you don’t use a
const
reference, that argument won’t be accepted by the compiler. As a very
simple example,
//: C11:Pasconst.cpp
// Passing references as const
void f(int&) {}
void g(const int&) {}
int main() {
//! f(1); // Error
g(1);
} ///:~
The
call to
f(1)
produces a compiler error because the compiler must first create a reference.
It does so by allocating storage for an
int,
initializing it to one and producing the address to bind to the reference. The
storage
must
be a
const
because changing it would make no sense – you can never get your hands on
it again. With all temporary objects you must make the same assumption, that
they’re inaccessible. It’s valuable for the compiler to tell you
when you’re changing such data because the result would be lost
information.
Pointer
references
In
C, if you wanted to modify the
contents
of the pointer rather than what it points to, your function declaration would
look like
and
you’d have to take the address of the pointer when passing it in:
int i = 47;
int* ip = &i;
f(&ip);
With
references in C++, the syntax is cleaner. The function argument becomes a
reference to a pointer, and you no longer have to take the address of that
pointer. Thus,
//: C11:Refptr.cpp
// Reference to pointer
#include <iostream>
using namespace std;
void increment(int*& i) { i++; }
int main() {
int* i = 0;
cout << "i = " << i << endl;
increment(i);
cout << "i = " << i << endl;
} ///:~
By
running this program, you’ll prove to yourself that the pointer itself is
incremented, not what it points to.
Argument-passing
guidelines
Your
normal habit when passing an argument to a function should be to pass by
const
reference. Although this may at first seem like only an efficiency concern
(and you normally don’t want to concern yourself with efficiency tuning
while you’re designing and assembling your program), there’s more
at stake: as you’ll see in the remainder of the chapter, a
copy-constructor is required to pass an object by value, and this isn’t
always available.
The
efficiency savings can be substantial for such a simple habit: to pass an
argument by value requires a constructor and destructor call, but if
you’re not going to modify the argument then passing by
const
reference only needs an address pushed on the stack.
In
fact, virtually the only time passing an address
isn’t
preferable is when you’re going to do such damage to an object that
passing by value is the only safe approach (rather than modifying the outside
object, something the caller doesn’t usually expect). This is the subject
of the next section.
Go to CodeGuru.com
Contact: webmaster@codeguru.com
© Copyright 1997-1999 CodeGuru