Introduction
I choose to write about references in C++ because I feel most of the people have misconceptions about references. I got this feeling because I took many C++ interviews and I seldom get correct answers about references in C++.
What is meant by references in C++? A reference is generally thought of as an aliasing of the variable it refers to. I hate the definition of references being an alias of a variable in C++. In this article, I will try to explain that there is nothing known as aliasing in C++.
Background
Both in C and in C++, there are only two ways by which a variable can be accessed, passed, or retrieved. The two ways are:
- Accessing/passing variable by value
- Accessing/Passing variable by address - In this case pointers will come into the picture
There is no 3rd way of accessing/passing variables. A reference variable is just another pointer variable which will take its own space in memory. The most important thing about the references is that it's a type of pointer which gets automatically dereferenced (by compiler). Hard to believe? Let's see....
A Sample C++ Code using References
Lets write a simple C++ code which will use references:
#include <iostream.h>
int main()
{
int i = 10;
int &j = i;
j++;
cout<< i << j <<endl;
cout<< &i << &j <<endl;
return 0;
}
References are nothing but constant pointers in C++. A statement int &i = j;
will be converted by the compiler to int *const i = &j;
i.e. References are nothing but constant pointers. They need initialization because constants must be initialized and since the pointer is constant, they can't point to anything else. Let's take the same example of references in C++ and this time we will use the syntax that the compiler uses when it sees references.
A Sample C++ Code using References (Compiler Generated Syntax)
#include <iostream.h>
int main()
{
int i = 10;
int *const j = &i;
(*j)++;
cout<< i << *j <<endl;
return 0;
}
You must be wondering why I skipped the printing of address from the above example. This needs some explanation. Since reference variables are automatically dereferenced, what will happen to a statement like cout << &j << endl;
. The compiler will convert the statement into cout << &*j << endl;
because the variable gets automatically dereferenced. Now &*
cancels each other. They become meaningless and cout
prints the value at j
which is nothing but the address of i
because of the statement int *const j = &i;
.
So the statement cout << &i << &j << endl;
becomes cout << &i << &*j << endl;
which is similar to printing the address of i
in both the cases. This is the reason behind the same address being displayed while we try to print normal variables as well as reference variables.
A Sample C++ Code using Reference Cascading
Here we will try to look at a complex scenario and see how references will work in cascading. Let's follow the code below:
#include <iostream.h>
int main()
{
int i = 10;
int &j = i;
int &k = j;
int &l = k;
cout<< i << "," << j << "," << k << "," << l <<endl;
j++;
cout<< i << "," << j << "," << k << "," << l <<endl;
k++;
cout<< i << "," << j << "," << k << "," << l <<endl;
l++;
cout<< i << "," << j << "," << k << "," << l <<endl;
return 0;
}
A sample C++ Code Using Reference Cascading (Compiler Generated Syntax)
Here we will see if we won't depend upon the compiler to generate constant pointers in place of reference and auto dereferencing the constant pointer, we can achieve the same results.
#include <iostream.h>
int main()
{
int i = 10;
int *const j = &i;
int *const k = &*j;
int *const l = &*k;
cout<< i << "," << *j << "," << *k << "," << *l <<endl;
(*j)++;
cout<< i << "," << *j << "," << *k << "," << *l <<endl;
(*k)++;
cout<< i << "," << *j << "," << *k << "," << *l <<endl;
(*l)++;
cout << i << "," << *j << "," << *k << "," << *l <<endl;
return 0;
}
A Reference Takes its Own Space in Memory
We can see this by checking the size of the class which has only reference variables. The example below proofs that a C++ reference is not an alias and takes its own space into the memory.
#include <iostream.h>
class Test
{
int &i;
int &j;
int &k;
};
int main()
{
cout<< "size of class Test = " << sizeof(class Test) <<endl;
return 0;
}
Conclusion
I hope that this article explains everything about C++ references. However I'd like to mention that C++ standard doesn't explain how reference behaviour should be implemented by the compiler. It's up to the compiler to decide, and most of the time it is implemented as a constant pointer.
Additional Notes to Support this Article
In the discussion forums for this article, people were having concerns that References are not constant pointers but aliases. I am writing one more example to support this fact. Look carefully at the example below:
#include <iostream.h>
class A
{
public:
virtual void print() { cout<<"A.."<<endl; }
};
class B : public A
{
public:
virtual void print() { cout<<"B.."<<endl; }
};
class C : public B
{
public:
virtual void print() { cout<<"C.."<<endl; }
};
int main()
{
C c1;
A &a1 = c1;
a1.print();
A a2 = c1;
a2.print();
return 0;
}
The example using references supports the virtual mechanism, i.e. looking into the virtual pointer to get the handle to correct function pointer. The interesting thing here is how the virtual mechanism is supported by the static
type which is simply an alias. Virtual mechanism is supported by dynamic information which will come into the picture only when a pointer is involved. I hope this will clarify most of the doubts.