How to Use C++ Reference Variables (C++ Reference Vs Pointer Example)

by Himanshu Arora on May 10, 2013

The concept of references in C++ is simple, interesting and useful to programmers.

It adds value to C++ when compared with C. Though the debate between the followers of C and C++ will always be there but I personally think that both the languages have little overlapping area of usage.

When compared to C, C++ has some very unique features that come in very handy. For example, the concept of references. In this tutorial, we will discuss the reference concept through some practical examples.

What is a Reference?

If I had to summarize the definition of a reference in one line then it would be :

A second name of an existing variable.

It’s like an alias for existing variable. This means that the original variable and the reference both refer to same value and the real beauty of a reference lies in the fact that you can do the same operations on the value through a reference as you could do with the original variable.

If you are new to C++, you should first read about C++ classes and objects.

The Semantics

The following example shows how you can declare a reference in C++

int &var = <some-variable>;

Yes, it’s a very simple syntax with ‘&’ telling the compiler that ‘var’ is a reference variable. But, can you figure out why have explicitly used <some-variable> in the syntax above?

Well, there is a strong reason behind it. Lets take an example to understand this point :

#include <iostream>

int main(void)
{
   int &var;

   /* Assume some logic here*/

   return 0;
}

The program shown above is a very simple one. We have not focused on what the program does as we want you to focus on the declaration of reference variable ‘var’.

Now, lets compile this code and see what the compiler has to say :

$ g++ ref.cpp -o ref
ref.cpp: In function ‘int main()’:
ref.cpp:5:9: error: ‘var’ declared as reference but not initialized

Oops…the compiler threw an error saying that ‘var’ is not initialized. Yes, that’s what we wanted to convey that references have to initialized at the time of declaration.

So here is the the same program but with correct declaration and some logic involving the reference :

#include <iostream>

int main(void)
{
   int a = 10;
   int &var = a;

   var = var + 1;

   std::cout<< "\na = "<< a <<"\n";

   return 0;
}

So you see that this time the reference variable ‘var’ was initialized with integer variable ‘a’.

Now lets compile and execute this program :

$ g++ ref.cpp -o ref
$ ./ref
a = 11

So, this time the program compiled without any errors. Also, if you try to map the logic with output, you’ll find that an increment operation on reference variable ‘var’ affected the value of  variable ‘a’. This proves that the reference variable and the original variable refer to the same memory location and hence work on the same value.

On a related note, you also might want to understand how to mix C and C++.

When to Use References?

As with any other programming concept, you should know when to use references to make the most of out of it. I’ll explain here one of the most important scenario in which references should be used.

While working with classes in C++, there are times when you have to pass a class object as an argument to some function. Someone with little or no knowledge of references would pass the object by value. But, do you know that pass by value method is very expensive as all the object data is copied from one variable to the other variable.

With the use of reference, you can define/declare the called function as :

int func(const SomeClass& obj_recv)
{
 /*....
   ....
   ....
 */
}

and call the function as :

func(obj_send);

This way, no copy takes place, just the object ‘obj_recv’ references the same memory (hence the same data) that belongs to the object ‘obj_send’.

So this way you can increase the processing speed of your program significantly by using references.

References and Pointers

While going through last section, some one with good knowledge of pointers could ask that the same thing can be done through pointers also. If this is what struck your mind then you are correct. We can achieve the same through the use of pointers also but references are far more simplified than using pointers.

Though the fact is references are implemented internally in the language through pointers only but the sole purpose of bringing in references in C++ is to leave aside the confusion of pointers.

Here are some of the points that describe why using references is easier :

  • References can be used like normal variables
  • Memory management of references left up to compiler
  • Much cleaner and readable code

You can see the difference between readability and cleanliness of code in the following example that swaps two variables using references and pointers both.

Using References :

#include <iostream>

void swap(int &a, int &b)
{
    int temp = 0;
    temp = a;
    a = b;
    b = temp;

    return;
}

int main(void)
{
   int a = 10;
   int b = 20;

   std::cout<< "\na = "<< a <<"\n"<<"b = "<<b<<"\n";

   swap(a,b);

   std::cout<< "After swap";
   std::cout<< "\na = "<< a <<"\n"<<"b = "<<b<<"\n";

   return 0;
}

Using Pointers :

#include <iostream>

void swap(int *a, int *b)
{
    int temp = 0;
    temp = *a;
    *a = *b;
    *b = temp;

    return;
}

int main(void)
{
   int a = 10;
   int b = 20;

   std::cout<< "\na = "<< a <<"\n"<<"b = "<<b<<"\n";

   swap(&a,&b);

   std::cout<< "After swap";
   std::cout<< "\na = "<< a <<"\n"<<"b = "<<b<<"\n";

   return 0;
}

Things to Take Care

Though references are fairly easy to understand and use, there are some points to take care of :

  • Do not initialize a reference variable with a constant value. This means int &var = 10 is not a valid initialization.
  • Do not return a reference from a function as the memory address that is referred by the reference variable would scope out once the function is done it’s execution.
  • Avoid using references to variables whose memory are dynamically allocated as it might create unnecessary confusion regarding the clean-up of that memory.

Linux Sysadmin Course Linux provides several powerful administrative tools and utilities which will help you to manage your systems effectively. If you don’t know what these tools are and how to use them, you could be spending lot of time trying to perform even the basic administrative tasks. The focus of this course is to help you understand system administration tools, which will help you to become an effective Linux system administrator.
Get the Linux Sysadmin Course Now!

If you enjoyed this article, you might also like..

  1. 50 Linux Sysadmin Tutorials
  2. 50 Most Frequently Used Linux Commands (With Examples)
  3. Top 25 Best Linux Performance Monitoring and Debugging Tools
  4. Mommy, I found it! – 15 Practical Linux Find Command Examples
  5. Linux 101 Hacks 2nd Edition eBook Linux 101 Hacks Book

Bash 101 Hacks Book Sed and Awk 101 Hacks Book Nagios Core 3 Book Vim 101 Hacks Book

{ 13 comments… read them below or add one }

1 SeattleC++ May 10, 2013 at 9:45 am

You can return a reference from a function; just not a reference to a local variable, because that particular reference will go out of scop and point to garbage.

2 A. Baines May 10, 2013 at 10:28 am

I’m still learning so all this is great for me. I get to try all this and see what everyone else is talking about. Keep it coming!

3 bob May 10, 2013 at 1:08 pm

Good article.

What would I do it I want to assign a pointer variable to a reference variable? Does it work? Give an example

4 MrScott May 10, 2013 at 1:56 pm

I have been programming in C/C++ for years but (probably like many others) I like these little tutorials because they give a “quick and dirty” run-through of basic concepts.

5 krmayogi May 10, 2013 at 4:40 pm

Actually, we recommend that a few of class getter methods return ‘const &’. A very basic example using an std::string (we use this mostly for std::map and std::vector)

class A
{
public:
A(const string &s)
: m_string(s)
{}

const string& getString()
{
return m_string;
}
private:
std::string m_string;
};

This avoids unnecessary copy of data.
const string& s1 = a.getString(); // no copy of data! Works great
However, if some non-const variable actually wants to modify the data, the compiler would be happy to allow it and copy the data this time:
string s2 = a.getString();
s2 += ” something else”.

Another very useful characteristic of using references in method parameters is that they don’t need to be checked for ‘null’, since as explained in the article, they can only be initialized with actual variable references, making them more easy to use than pointers.
Also a const string& str means str can not be changed, period.
A const char * str puts even intermediate developers in doubt about what exactly is const (pointer or data).
However, when you’re dealing with dynamic memory allocations (malloc, new etc), pointers take the cake.

Remeber friends: const references are your friends!

6 krmayogi May 10, 2013 at 4:45 pm

Obviously, the life-time of the object has to be considered. A class whose objects are short-lived, is better off just returning copy of it’s members.
But manager objects who stay alive throughout, can manage to return a reference!

7 SeattleC++ May 13, 2013 at 10:14 am

Bob,
To understand assigning pointers to references, it helps to understand rvalues and lvalues.

In the assignment statement “i = 3;”, 3 is an rvalue, a literal integer 3. i is an lvalue. That is, i is the address of an integer. The thing on the left of an assignment statement has to be an lvalue, and the thing on the right has to be an rvaule. (They get their name from the left and right side of the assignment).

In the statement “i = j;”, j starts out as an lvalue, the address of an int value. The compiler loads the contents of that address to create an rvalue. Then the compiler stores the rvalue into the lvalue address of i.

If pi is of type pointer-to-int, the statement “i = *p;” works as follows. p starts out as an lvalue of type pointer-to-int. The “*” operator dereferences the pointer, that is, it loads the value at the address p. This value is the address of an int, so effectively the compiler has converted an lvalue of type pointer-to-int into an lvalue of type int. The compiler needs an rvalue, so it loads the value at the address it just computed. Then it saves the int rvalue into the address of the lvalue i.

Now lets look at assigning a pointer to a reference. If ri is type int&, then you can write the assignment “ri = *p;” References behave in some ways as if they were pointers. So, p is an lvalue of type pointer-to-int. *p is an rvalue of type pointer-to-int. This rvalue can be assigned directly to ri, which is an lvalue of type pointer-to-int.
To answer your question (finally) the way you assign a pointer to a reference is to dereference the pointer with the “*” operator.

8 DuskoKoscica May 31, 2013 at 12:06 am

This is important lesson, it has very good usage in OOP, as well as functions.

Once I have done some functions for evaluating the value of integral, and numerical methods were used.
But it was so hard to read that, just because I have used a lot of * for pointers, the problem was even more difficult just because there were a lot of * for multiplications….

Good to learn this thing!

9 madni October 2, 2013 at 3:33 am

My question is if i am passing the parameter by reference than,should i return these variable(s) ? or changing any variable in function definition would change it automatically ?

best (Y) clear my mind about function call and related stuff good one

10 SeattleC++ October 2, 2013 at 3:44 pm

madni; a reference is implemented as a pointer. If you pass a reference to something into a function and then change that thing, it’s changed globally. You don’t have to return it. It’s just like if you passed in a pointer, then de-referenced the pointer to change the pointed-to value.

11 Andrew Carmichael March 3, 2014 at 10:02 am

Anyone know how to create a reference to an object that has an address in absolute memory ? Eg an integer register at address 0x1DF800

12 SeattleC++ March 4, 2014 at 12:25 am

This is advanced stuff, only for the stout-hearted. And it’s likely to be undefined behavior. Try this statement.

int& foo = *(int*)0x1DF800;

That is, cast 0x1DF800 to an int* rvalue, then dereference it with the * operator to get an int lvalue, which can initialize the reference. The compiler emits instructions that sets the pointer that internally represents foo to 0x1DF800.

If the value read at that location is a hardware register that can change without action of the program, better cast 0x1DF800 to (volatile int *) or the compiler may cache a previously read value and not re-read the register.

13 SeattleC++ March 4, 2014 at 12:46 am

Just to be clear,

volatile int& foo = *(volatile int*)0x1DF800;

Leave a Comment

Previous post:

Next post: