References in C++

sources C++ Primer book and https://www.geeksforgeeks.org/references-in-c/

What are references in C++?

EXAMPLE:

int myVal = 1024;
int &refVal = myVal;     refVal refers to (is another name for) myVal
int &refVal2;                 //error: a reference must be initialised

Once initialised a reference remains bound to its initial object, there is no way to rebind a reference to refer to a different object. Because there is no way to rebind, references must be initialised.

A Reference Is An Alias

A reference is not an object. Instead, a reference is just another name for an already existing object

After a reference has been defined, all operations on that reference are actually operations on the object to which the reference is bound:

refVal = 2; //assigns 2 to the object to which refVal refers, ie. to myVal int refVal2 = refVal; //same as refVal2 = myVal

When we assign to a reference, we are assigning to the object to which the reference is bound. When we fetch the value of a reference, we are really fetching the value of the object to which the reference is bound. Similarly, when we use a reference as an initialiser, we are really using the object to which the reference is bound:

int &refVal3 = refVal;     //refVal3 is bound to the object to which refVal is bound ie. to myVal

int i = refVal;  //initialises i from the value in the object to which refVal is bound
                       // initialises i to the same value as myVal

Because references are not objects, we may not define a reference to a reference.

Reference Definitions

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

int i = 1024, i2 = 2048;    //i and i2 are both ints
int &r = i , r2 = i2;             //r is a reference bound to i, r2 is an int
int i3 = 1024, &ri = i3;      // i3 is an int, ri is a reference bound to i3
int &r3 = i3, &r4 = i2;       //both r3 and r4 are references

With two exceptions (I will cover these later), the type of a reference and the object to which the reference refers must match exactly. Moreover, a reference may be bound only to an object, not to a literal or to the result of a more general expression:

int  &refVal4 = 10;      //error: initialiser must be an object

double dval = 3.14;
int &refVal5 = dval     //error: initialiser must be an int object

Reference from a Pointer

int *n;     //pointer to n

int &m = *n;     //here we dereference the pointer *n* and get the reference to that

Applications of References

  1. Modify the passed parameters in a function: If a function receives a reference to a variable, it can modify the value of the variable. For example, the following program variables are swapped using reference:
#include<iostream>
using namespace std;

void swap (int& first, int& second)
{
    int temp = first;
    first = second;
    second = temp;
}

int main()
{
    int a = 2, b = 3;
    swap( a, b );
    cout << a << " " << b;
    return 0;
}

Output : 3 2

  1. Avoiding a copy of large structures: Imagine a function that has to receive a large object. If we pass it without reference, a new copy of it is created which causes wastage of CPU time and memory. We can use references to avoid this:
struct Student {
   string name;
   string address;
   int rollNo;
}

// If we remove & in below function, a new
// copy of the student object is created.
// We use const to avoid accidental updates
// in the function as the purpose of the function
// is to print s only.

void print(const Student &s)
{
    cout << s.name << "  " << s.address << "  " << s.rollNo;
}
  1. In For Each Loops to modify all objects : We can use references in for each loops to modify all elements:

#include <bits/stdc++.h>
using namespace std;

int main()
{
    vector<int> vect{ 10, 20, 30, 40 };

    // We can modify elements if we
    // use reference
    for (int &x : vect)
        x = x + 5;

    // Printing elements
    for (int x : vect)
       cout << x << " ";

    return 0;
}

Output : {15, 25, 35, 45 }

  1. For Each Loop to avoid the copy of objects: We can use references in each loop to avoid a copy of individual objects when objects are large:

#include <bits/stdc++.h>
using namespace std;

int main()
{
    vector<string> vect{"geeksforgeeks practice",
                     "geeksforgeeks write",
                     "geeksforgeeks ide"};

    // We avoid copy of the whole string
    // object by using reference.
    for (const auto &x : vect)
       cout << x << endl;

    return 0;
}

References vs. Pointers

  1. The pointer variable has n-levels/multiple levels of indirection i.e. single-pointer, double-pointer, triple-pointer. Whereas, the reference variable has only one/single level of indirection. The following code reveals the mentioned points:

#include <iostream>
using namespace std;

int main() {
    int i=10; //simple or ordinary variable.
    int *p=&i; //single pointer
    int **pt=&p; //double pointer
    int ***ptr=&pt; //triple pointer
    // All the above pointers differ in the value they store or point to.
    cout << "i=" << i << "\t" << "p=" << p << "\t"
         << "pt=" << pt << "\t" << "ptr=" << ptr << "\n";
    int a=5; //simple or ordinary variable
    int &S=a;
    int &S0=S;
    int &S1=S0;
    cout << "a=" << a << "\t" << "S=" << S << "\t"
         << "S0=" << S0 << "\t" << "S1=" << S1 << "\n";
    // All the above references do not differ in their values
    // as they all refer to the same variable.
 }

OUTPUT: i=10 p=0x7ffee90dc61c pt=0x7ffee90dc610 ptr=0x7ffee90dc608 a=5 S=5 S0=5 S1=5

We can also see the output when the pointers are dereferenced in the code:

#include <iostream>
using namespace std;

int main() {
    int i=10; //simple or ordinary variable.
    int *p=&i; //single pointer
    int **pt=&p; //double pointer
    int ***ptr=&pt; //triple pointer
    // All the above pointers differ in the value they store or point to.
    cout << "i=" << i << "\t" << "p=" << *p << "\t"
         << "pt=" << **pt << "\t" << "ptr=" << ***ptr << "\n";

OUTPUT: i=10 p=10 pt=10 ptr=10

References are less powerful than pointers

1) Once a reference is created, it cannot be later made to reference another object; it cannot be reseated. This is often done with pointers.

2) References cannot be NULL. Pointers are often made NULL to indicate that they are not pointing to any valid thing.

3) A reference must be initialized when declared. There is no such restriction with pointers

Due to the above limitations, references in C++ cannot be used for implementing data structures like Linked List, Tree, etc. In Java, references don’t have the above restrictions and can be used to implement all data structures. References being more powerful in Java is the main reason Java doesn’t need pointers. References are safer and easier to use:

1) Safer: Since references must be initialized, wild references like wild pointers are unlikely to exist. It is still possible to have references that don’t refer to a valid location (See questions 5 and 6 in the below exercise )

2) Easier to use: References don’t need a dereferencing operator to access the value. They can be used like normal variables. ‘&’ operator is needed only at the time of declaration. Also, members of an object reference can be accessed with dot operator (‘.’), unlike pointers where arrow operator (->) is needed to access members. Together with the above reasons, there are few places like the copy constructor argument where pointer cannot be used. Reference must be used to pass the argument in the copy constructor. Similarly, references must be used for overloading some operators like ++.