Misunderstanding References in C plus

by Goran Stradew.

Share
|
Homepage | Submit your article | Contact | TOS
More articles on c  

You are here: Categories » Computers and technology » C

There are two common problems with references. First, they're often confused with pointers. Second, they're underused. Many current uses of pointers in C++ are really C holdovers that should be ceded to references.

A reference is not a pointer. A reference is an alias for its initializer. Essentially, the only thing one can do with a reference is initialize it. After that, it's simply another way of referring to its initializer. A reference doesn't have an address, and it's even possible that it might not occupy any storage:

int a = 12; 
int &ra = a;
int *ip = &ra; // ip refers to a
a = 42; // ra == 42

For this reason, it's illegal to attempt to declare a reference to a reference, a pointer to a reference, or an array of references. (Though the C++ standards committee has discussed allowing references to references in the future, at least in some contexts.)

int &&rri = ra; // error! 
int &*pri; // error!
int &ar[3]; // error!

References can't be const or volatile, because aliases can't be const or volatile, though a reference can refer to an entity that is const or volatile. An attempt to declare a reference const or volatile directly is an error:

int &const cri = a; // should be an error . . . 
const int &rci = a; // OK

Strangely, it's not illegal to apply a const or volatile qualifier to a type name that is of reference type. Rather than cause an error, in this case the qualifier is ignored:

typedef int *PI; 
typedef int &RI;
const PI p = 0; // const pointer
const RI r = a; // just a reference!

There are no null references, and there are no references to void:

C *p = 0; // a null pointer 
C &rC = *p; // undefined behavior
extern void &rv; // error!

A reference is an alias, and an alias has to refer to something.

Note, however, that a reference does not have to refer to a simple variable name. It's sometimes convenient to bind a reference to an lvalue resulting from a more complex expression:

int &el = array[n-6][m-2]; 
el = el*n-3;
string &name = p->info[n].name;
if( name == "Joe" )
process( name );

A reference return from a function allows assignment to the result of a call. The canonical example of this is an index function for an abstract array:

template <typename T, int n> 
class Array {
public:
T &operator [](int i)
{ return a_[i]; }
const T &operator [](int i) const
{ return a_[i]; }
// . . .
private:
T a_[n];
};

The reference return permits a natural syntax for assignment to an array element:

Array<int,12> ia; 
ia[3] = ia[0];

References may also be used to provide additional return values for functions:

Name *lookup( const string &id, Failure &reason ); 
// . . .
string ident;
// . . .
Failure reasonForFailure;
if( Name *n = lookup( ident, reasonForFailure ) ) {
// lookup succeeded . . .
}
else {
// lookup failed. check reason . . .
}

Casting an object to a reference type has a very different effect from the same cast to the nonreference version of the type:

char *cp = reinterpret_cast<char *>(a); 
reinterpret_cast<char *&>(a) = cp;

In the first case, we're converting an integer into a pointer. (We're using reinterpret_cast in preference to an old-style cast, like (char *)a. The result is a copy of the integer's value, interpreted as a pointer.

The second cast is very different. The result of the cast to reference type is a reinterpretation of the integer object itself as a pointer. It's an lvalue, and we can assign to it. (Whether we will then dump core is another story. Use of reinterpret_cast generally implies "not portable.") An analogous attempt with a cast to nonreference will fail, because the result of the cast is an rvalue, not an lvalue:

reinterpret_cast<char *>(a) = 0; // error! 

A reference to an array preserves the array bound. A pointer to an array does not:

int ary[12]; 
int *pary = ary; // point to first element
int (&rary)[12] = ary; // refer to whole array
int ary2[3][4];
int (*pary2)[4] = ary2; // point to first element
int (&rary2)[3][4] = ary2; // refer to whole array

This property can be of occasional use when passing arrays to functions.

It's also possible to bind a reference to a function:

int f( double ); 
int (* const pf)(double) = f; // const pointer to function
int (&rf)(double) = f; // reference to function

There's not much practical difference between a constant pointer to function and a reference to function, except that the pointer can be explicitly dereferenced. As an alias, the reference cannot, although it can be converted implicitly into a pointer to function and then dereferenced:

a = pf( 12.3 ); // use pointer 
a = (*pf)(12.3); // use pointer
a = rf( 12.3 ); // use reference
a = f( 12.3 ); // use function
a = (*rf)(12.3); // convert ref to pointer and deref
a = (*f)(12.3); // convert func to pointer and deref

Distinguish references and pointers.

Leave a comment or ask a question
Total comments: 0

C Disclaimer

  • The e-articles directory is not responsible for any and all copyright infringements by writers and authors. If you suspect the information contained by this page for any copyright infringements, please contact us to investigate the issue

 
free content
    Copyright © 2006 - 2012 e-articles.info.
The texts, articles and tutorials in the directory are property of their respective owners and authors.