Monday 30 May 2016

References in C++

Nicknames. Who doesn't like a nickname? Those who don't have one. So, who doesn't have a nickname? No one.
Remember how we like to give nice and descriptive names to variables in our programs? I want you to ask yourself a question. If variables have names, can they have nicknames then? And to answer this question (that you asked because I asked you to do so), sure we can have nicknames for variables. 
If you're wondering why is there a need to have nicknames for variables? The answer lies in common sense. Nick names are short names, used as a convenience when calling someone very familiar to you. A computer program can also do the same. We can have a nickname for variables and use these nickname to read/ write and use this variable otherwise.
Speaking technically, in C/C++, a variable (which is nothing but a space in memory) can have multiple names associated with it. One of these names is the actual name, given at the time of declaring the variable. And other names are called references.
A reference to a variable/ object is another name for the same memory address. 
It is kind of like pointing to a variable but with modesty. 

int x=10;

int &y=x; //y is a reference.
//a reference must always be initialized. 
y++; //this will effect the same memory address as of x
cout<<x //11 will be printed.

A reference variable can only refer to one thing and one thing only for ever and ever. This means that if we try to assign some variable to a reference variable after it has been initialized then that only means we are assigning a value to the memory address to which our reference variable referred to.

int x=2, y=5;
int &r=x; //referring to x now and forever.
r=y; /*this does not mean that r refers to y now. It only means that what address r referred to, now contains the value of y.*/
cout<<x; //this will now print 5.

Call by Reference:
I was taught this whole concept of call by reference before I was told that there even existed something called a reference to a variable/ object. I can not begin to tell you how unfair that is. 
If I am being honest, call be reference can be done using pointers alone. But that is like forcing the issue a bit. 
void twice(int *i)
{
      *i= 2*(*i);
}
int main()
{
      int x=10;
      twice(&x); 
      /*after this statement, changes made by twice() will reflect         permanently to x*/
     return 0;
}
The above of course is an example where the changes made by a function are not bound only to the parameters in the function body. They are reflected back to the original arguments as well. This primarily happens because instead of passing a copy of the variable as argument, we pass an address to the functions. And we know how addresses can be so final. 

C++ has a neater way of doing this. This involves the use of references and not pointers. The secret lies in the way in which we define the function:

void twice(int &x)
{
     x=2*x;
}
int main()
{
      int x=10;
      twice(x); /*notice how we don't even need to pass address                            even*/
      return 0;
}

So what happened above? In the function definition, the formal parameter is a reference. A reference which has not yet be initialized (this may sound wrong but it's not). So when we call the function, we pass a variable and not it's address. This variable is then made to be referred by the reference variable in the function definition. And hence the changes made to the reference variable also reflect to the original variable.

The reason why we can have an uninitialized reference variable in a function definition is because variables that are local to a function don't really come into play until they are called. So we can let them be a little spoiled as long as they are not active. And by the time they need to come in action (the function call ), we initialize them immediately (by passing a variable as an argument).

Returning References from Functions:
A function returning a value is like a variable carrying a value. This obviously means that we can use a function (which returns a value) on the right hand side of an expression. 
int func(int x); //assume this function returns some int value.
We can use the above function like so:
int a= func(2);//the value returned by func() directly goes into a
We can even do things like:
int a= 2+3-func(2);

But what if the function returns a reference? In that case we can actually assign something to the value returned by the object. Think of it as this, object returns a nickname of something. We can assign this nickname an actual variable. Basically the function will return a bucket (empty/ full) and we can fill or replace the contents of the bucket.

To understand where this can be used consider the following:
Imagine, a string "govind.sharma". Our job is to replace the dot '.' with a space.
One way to do this would be using a function that returns a reference to the position in the string that contains a dot. And then this reference can be used to assign the value ' ' (space) to the place where the dot was earlier. To do this we can define a function that finds where the dot is and returns a reference to position in the string.

char &dot(string &x) /*The & sign after indicates that this function will be returning a reference to a char. */
{
      for(int i=0; i<x.length(); i++)
      {
            if(x[i]=='.')
            {
                   return x[i]; /*instead of passing the value of x[i], this                                         will return the bucket containing x[i]                                             (for now, we will replace it with a space                                         in a bit)*/
            }
      }
}
int main()
{
      string x;
      x="govind.sharma";
      dot(x)=' '; //change the contents of the bucket
      cout<<x;   //check out the results
      return 0;
}

It is not a given that returning a reference can work only with functions using call by reference (like in this case). It's just what the solution needed. It's not a necessity. 

References to Derived Types:
Similar to the situation as described for pointers in the other post, a base class reference can be used to refer to an object of derived class. The most common application of this is found in function parameters. A base class reference parameter can receive objects of the base class as well as any other type derived from that base.


Restrictions to References:
1) You can not reference another reference. Put differently, you can not obtain the address of a reference.
2) You can not create arrays of references.
3) You can not have a pointer to a reference.
4) You can not reference a bit-field.
5) A reference variable must be initialized when it is declared unless it is a member of a class, a function parameter, or a return value.
6) Null references are prohibited.








No comments:

Post a Comment