What Are References in C++?

Using C++ references is similar to the use of constant pointers. A reference automatically gets dereferenced to the location it point to. It is typically used in association with function argument list and return values. It has many other uses apart from this; a reference also can be used as a freestanding reference. This article explores some of the key concepts of references found in C++.

Overview

What sets C/C++ apart from most programming languages is the use of pointers. Pointers empower programmers to optimize the code right from where it all begins—coding. But, be cautious though; pointers can be misused, less optimally used, even overused. In contrast to pointers, a reference defines an alternative name for an object. The apparent look of a reference variable may not be like a pointer, yet it must be understood that it is also a pointer in another form.

Unlike the use of references found in Pascal, C++’s version was derived from Algol, particularly to aid in the syntax support for operator overloading. It has many uses, especially to leverage the general convenience of the C++ programmer. Note that C++ references are nothing but pointers in a new shape. So, to delve into the arena, we must at least give a hint of what a pointer is.

Pointers

We can declare many types of variables associated with a particular type such, as short, int, char, float double, and so forth. They are used to hold some data value; pointers, on the other hand, also are variables with a specific data type but hold the location or address of the value stored in memory. For example, consider this term:

int i = 10;   // Is a simple integer variable

This means that ‘i’ is an integer type variable with the content value 10. This also means that ‘i’ must be allocated in a memory area which has a specific address (say, 100). Therefore, when we write

// Pointer is assigned with the address of i i.e. &i
int *pi = &i;

It means that ‘pi’ is an integer type pointer holding the location of ‘i’ (for example, 100) as its value. We also can simply declare a pointer without initializing it; however, it is more appropriate to initialize it, as a good programming practice.

int *pi2;        // OK, but better to initialize it.

int *pi3 = 0;    // More appropriate, same as int *pi3 = NULL.

There is a subtle difference between pointers declared in C and the pointers in C++. The C++ pointers are more strongly typed. C does not allow casual conversion of one pointer type to another, yet there is a way we can achieve this goal by declaring a pointer as void*, an implicit approach. This type of implicit conversion is not allowed in C++ and flags an error message if we try. However, if we must, C++ supports explicit conversion, using cast. This allays one less misuse of pointers inherited from C.

References

In C++, a reference may be deemed as an alias name for an object. We define it by writing a declarator with an ampersand symbol. For example,

int ivar = 10;
int &refVar = ivar;

This means that refVar refers to ivar or, in other words, ivar now has another name, called refVar. But, note that if we write:

int &refVar2;   // Error

this is an error, because we cannot declare an uninitialized reference variable. As we initialize a variable, the value of the initializer is copied to the object we are creating. But, when we define a reference, we are actually binding the reference to its initializer rather than copying the value. The reference remains bound to its initial object from the time of its creation until the initializer is destroyed. There is no way to “unbound” a reference and bind it to another object in its lifetime. This is exactly the reason why C++ compilers do not allow the creation of uninitialized references. Therefore, a reference is never an object but an alias, or another name for an already existing object.

Freestanding References and Pointers

A reference is bound to some other variable’s storage. This makes it behave like a pointer. Because this binding is irreversible, it behaves like a constant. For example, following is a freestanding reference.

int k;
int &ref_k = k;

References must refer to someone else’s location once declared. Following is also a valid reference declaration for obvious reasons.

const int &const_ref = 100;

Although it apparently seems to refer to value 100, in actuality it refers to the anonymous location to the value 100. As we access the reference, we are actually accessing the location, just as with pointers. A significant exception with the pointer is that:

  • References must be initialized; otherwise, the compiler flags an error.
  • We need not bother how to dereference it; the compiler does it for us.

Therefore, if we draw the contrast with pointers, it would be like this:

References Pointers
Must be initialized May be initialized (optional)
Once bound, are irreversible, unchangeable Pointers pointing to any object are changeable at any time
Are always legit; they cannot be NULL Can be NULL

References in Functions

One of the most common uses of references in C++ is in association with function arguments and return values. This is because any changes made to the value in the function arguments in the function are sustained even if the references go out of the scope of the function. This feature is not unique with references only; the use of pointers also can have the same effect but the code looks more cleaner with the use of references. Here is an example to illustrate that.

#include <iostream>
using namespace std;

int *getMultiplied(int *val, const int m)
{
   *val*=m;    // Equivalent to *val = (*val) * m
   return val;
}

int &getMultiplied(int &val, const int m)
{
   val*=m;    // Equivalent to val = val * m
   return val;
}

int main(){
   int x=10;

   getMultiplied(&x,10);    // OK
   cout<<"x = "<<x<<endl;

   getMultiplied(x,20);     // OK and also clean code
   cout<<"x = "<<x<<endl;

   return 0;
}

Observe that both functions do the same thing, but the one that uses a reference rather than a pointer actually looks much cleaner. The difference is that references simply hide the mechanics of pointer operation; internally, they are all the same to the compiler and dealt with in a similar manner.

Pointer References

Suppose we need to modify the contents of the pointer rather than the object it points to; then, we typically use double pointers. Dealing with pointers that dereference two or more times is a call for trouble in most occasion. Double and more than double pointers can be challenging to deal with. They make the code complex and difficult to maintain and debug. Also, the code can look extremely unclean and obfuscated. But, in cases where we cannot avoid them, we can use references to make the code look a little saner. For example, a function that takes the content of the pointer as an argument, we typically write it as follows;

void function(int **arg);

and we can invoke the function as,

int ival = 12;
int *pi = &ival;
function(&pi);    // Sending the address of pi

We can modify this code to do the same thing, but in a little cleaner way with C++ reference as follows.

void function(int*& arg);
int ival = 12;
int *pi = &ival;
function(pi);

Conclusion

A reference is primarily an alias of an object. Although apparently they seem like regular variables, they are actually pointers in another form. These are a few bits of key ideas to begin with; an in-depth study will reveal more. Once one is familiar with the basics, we can try a more advanced topic which may be a bit more confusing concepts in association with references. We’ll take them up in another article soon. Stay tuned.

Manoj Debnath
Manoj Debnath
A teacher(Professor), actively involved in publishing, research and programming for almost two decades. Authored several articles for reputed sites like CodeGuru, Developer, DevX, Database Journal etc. Some of his research interest lies in the area of programming languages, database, compiler, web/enterprise development etc.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read