Reference Types


The syntax of reference declarations was presented in the section on declarators. This section gives further information on the subject of reference declarations.

Syntax

Given a type T, a reference to T may be declared as follows.

reference_declaration:
       T& constant_volatile_qualifier_list reference_name;
 
constant_volatile_qualifier_list:
       constant_volatile_qualifier constant_volatile_qualifier_list
 
constant_volatile_qualifier:
        const
        volatile

Notes

The following restrictions apply to references:

Additionally, a declared reference must be initialized unless:

A reference to an object is similar to a pointer to an object in the sense that if what is referenced is changed (or in the case of pointer, if what is dereferenced is changed) then the original object is changed. The difference is that pointers require referencing and dereferencing; whereas references do not. For example, consider the following code fragment.

int i=0;
int& ri = i;   // reference to i (no address required when initializing)
int* pi = &i;  // pointer to i (address required when initializing)
ri += 2;       // no dereferencing required - changes the value of the variable i
*pi += 2;      // dereferencing required - also changes the value of the variable i

If a reference to an object is passed to a function and that function modifies the object through the reference then the original (referenced) object is changed. For example, consider the following code fragment.

void f(int& i) {i++;}   // because reference, the referenced object is updated
 
int j=0;
f(j);                   // pass a reference to integer j
cout << j;              // prints out 1 because j is changed

Functions may also return references. Consider the following mini-string class.

struct string
{
  string(const char* copy);            // Allocates storage and copies string
 ~string();                            // Frees storage
 
  char& operator[](unsigned index) // Note: returns a reference to the storage
  {return s[index];}
 
  char* s;
};
 
string::string(constant char* copy)
{
 s = new char[strlen(copy)+1];
 strcpy(s,copy);
}
 
string::~string() {delete [] s;}

Note how the subscript operator (operator[]) returns a reference to a character. As shown above, the subscript operator returns a reference to storage pointed to by s (storage that is allocated by the constructor and freed by the destructor). Thus, an instance of the mini-string class can be used as follows.

string test("Hello World");
if (test[0] == 'H')                        // Note: operator[] returns reference to character
 cout << "H was found - just as expected"; // This is the branch that is taken
else
 cout << "Something is wrong";             // Should never get here

A reference to a pointer is a useful device. Consider the following portion of a Balanced Binary Tree class declaration.

class tree
{
  ...
  decision add(node*& root); 
  decision remove(node*& root);
  ...
};

The methods add and remove potentially change the pointer to the root node of the tree, hence these methods accept a reference to the root node pointer. References to constant objects are also very useful devices, and the compiler is able to create temporary objects in certain circumstances because of them (see initializing references). When a reference to a constant class instance is used, only constant methods of the class may be issued. For example, consider the following code fragment.

class test
{
 public:
  int i;
 
  int f() const {return i;}  // constant function - does not alter i
  int g() {return i++;}      // non-constant function - does alter i
};
 
test testA;
const test& reference = testA;     // reference to constant test
 
int i = reference.f();  // Correct because f is a constant function
int j = reference.g();  // Syntax error - g is a non-constant function