Functions and References

Fucntions and references in C++

Last Reviewed and Updated on February 7, 2020
Posted by Parveen(Hoven),
Aptitude Trainer and Software Developer

My C/C++ Videos on Youtube

Here is the complete playlist for video lectures and tutorials for the absolute beginners. The language has been kept simple so that anybody can easily understand them. I have avoided complex jargon in these videos.

In simplest terms, a reference is an alterate, alias name for an existing object or a variable. A reference is always to a concrete existing memory that contains an object.

Syntax of a reference

When a reference to an object or a variable is created, that object must be a living object; it is not possible to declare a reference and postpone its initialization to a later time. The & operator is used to create a reference. In the following code, a reference called ref is being created to a variable called x. The data type of a reference is same as the object to which it refers.

int main()
{
    int x = 9;
 
    int& ref = x;
 
    return 0;
}

A reference can be used to manipulate a variable, as if it were the variable itself. There is absolutely no behavioural difference between a reference and the variable it references. The output in the code below displays 10. Incrementing the reference is same as incrementing the variable itself.

int main()
{
    int x = 9;
 
    int& ref = x;
 
    ref++;
 
    cout << x << endl;
 
    return 0;
}

Reference cannot be re-referenced

A reference cannot be null or un-initialized. It must be initialized when it is first introduced. That initial value sticks to it permanently. It is not possible to make it refer to something else. First of all. let us verify that a reference cannot be declared and left un-initialized. The compiler will raise an error.

int main()
{
    int x = 9;
 
    // ERROR
    int& ref;
 

    return 0;
}

A reference cannot be later made to reference another variable or set to a null value. It is impossible, as you can see in this code, where we are trying to set ref to y, and the real effect is that y gets assigned to x because of the reference. It is not possible, even if we try to do it.

int main()
{
    int x = 9;
 
    int& ref = x;
 
    int y = 88;
 
    // DOESN'T CREATE A 
    // REFERENCE TO y
    // actually, it is setting x equal to y !
    ref = y;
 
    // prints 88
    cout << "x = " << x << endl;
 
    return 0;
}

References are safe

References are very safe in C++ because they cannot contain invalid data, as they have to be initialized properly at the time of creation. The second more important thing is that a reference is always initialized at the design time, i.e., at the time of writing the program. Hackers and malicious programs attach themselves to your code at run time. They insert harmful code into your executable through pointers of your program, and as we know, pointers do not have to be initialized at the time of declaration, and, unlike references, they can be used to store addresses of objects at runtime. Malicious code operates by creating harmful objects and storing their address in those pointers and cause damage.

lvalue and rvalue References

The reference we have discussed so far is called an lvalue reference. It is a reference to a variable that has a name, a variable that is already defined. But a compiler often creates temporary variables. Such temporary variables do not have a name. Consider the code below. Here the function fx returns an int. But if we merely call that function without storing the returned int in a variable, then the returned value is created as a temporary variable, and discarded.

int fx()
{
    int x = 89;
 
    return x;
}
 
int main()
{
    // the returned value is stored in 
    // a temporary location and gets 
    // discarded
    fx();
 
    return 0;
}

The above story can be verified by writing a class with a default constructor and a copy constructor. The following code shows that two constructions take place. The second one is when the returned object is copied into a temporary.

class CAbc
{
public:
    CAbc()
    {
        cout << "ctor called" << endl;
    }
 
public:
    CAbc(const CAbc& cc)
    {
        cout << "copy-ctor called" << endl;
    }
};
 
CAbc gx()
{
    CAbc cab;
    return cab;
}
 
int main()
{
    gx();
 
    return 0;
}

If we store the returned value into a variable, then the compiler directly copies the return value into the new location without creating a temporary variable. In the following code no temporary is created because the output shows only two constructions, and not three. When a compiler skips the creation of a temporary, then it is called return value optimization.

class CAbc
{
public:
    CAbc()
    {
        cout << "ctor called" << endl;
    }
 
public:
    CAbc(const CAbc& cc)
    {
        cout << "copy-ctor called" << endl;
    }
};
 
CAbc gx()
{
    CAbc cab;
    return cab;
}
 
int main()
{
    CAbc cc = gx();
 
    return 0;
}

The issue right now is: how to obtain a reference to a temporary? A temporary has no name, there is no identifier associated with it. But from the above code and verification we know that they do exist. So how to obtain a reference to them? C++ provides an rvalue reference for this case, we can see it in the code written below. An rvalue reference is created by using two && together, just to emphasize the fact that is a different type of a reference.

class CAbc
{
public:
    CAbc()
    {
        cout << "ctor called" << endl;
    }
 
public:
    CAbc(const CAbc& cc)
    {
        cout << "copy-ctor called" << endl;
    }
};
 
CAbc gx()
{
    CAbc cab;
    return cab;
}
 
int main()
{
    // rvalue reference with two &&
    CAbc&& cc = gx();
 
    return 0;
}

You might be willing to ask, what is the use of rvalue references. Where are they used, where should I use them. It's a bit beyond the scope of this article, but just for your curiosity rvalue references help us write highly optimized code when we have to deal with large arrays of classes and structs containing a large number of data members.

Arrays of References

Arrays of references are not possible in C++. As you can see the following code gives an error.

int main()
{
    // ERROR, cannot create 
 // an array of references
    int& arr[] = {3, 6};
    
    return 0;
}

What is a Reference basically?

A reference stores address of another variable, just like we know pointers that store addresses of another objects. A reference is an already de-referenced pointer, with a star applied to the address - like *p. It is safe as compared to a pointer for the reasons already explained above. A reference is more of a compiler trick, but it has a cleaner syntax, and is extremely safe.

Function Parameters as References

Functions can take references as parameters. In the following example, the function fx takes an int reference as a parameter - fx (int & x). Since the parameter is an alias, any changes to the parameter inside the body of the function take place on the variable back home. So when y is passed to the function fx that alters its argument, and y is printed after the function call, the new value of 11 is printed.

void fx(int& x)
{
    x++;
}
 
int main()
{
    int y = 10;
 
    fx(y);
 
    // prints 11
    cout << y << endl;

    return 0;
}

How is it possible that an int argument can be passed to a function that accepts an int& parameter? The answer is that there is a standard conversion for each type to its reference. In other words, you can say that the compiler creates a reference to the type and passes that reference to the function. This is a behind-the-scenes activity.

Passing objects by reference can improve performance if the objects are heavy, ie, have a large size. To understand this consider a struct of 1kb size, and let us say that it's object is being passed to a function by value, and some changes are made and then returned through the return statement. This whole process involves two un-necessary object of the struct. This can be seen by running the following program.

struct S
{
public:
    // copy c-tor
    S(const S&)
    {
        cout << "copy c-tor called..." << endl;
    }
 
public:
    // default c-tor
    S()
    {
        cout << "c-tor called..." << endl;
    }
};
 
S fx(S param)
{
    // some changes made ...

    // ... and then returned
    return param;
}
 
int main()
{
    S obj;
 
    fx(obj);
 
    return 0;
}

But if the function fx is modified slightly, and everything else is kept the same, we can meet the same objective without the overhead of creating un-necessary objects. Run the following code to see this.

struct S
{
public:
    S(const S&)
    {
        cout << "copy c-tor called..." << endl;
    }
 
public:
    S()
    {
        cout << "c-tor called..." << endl;
    }
};
 
void fx(S& param)
{
    return;
}
 
int main()
{
    S obj;
 
    fx(obj);
 
    return 0;
}

Returning References from Functions

A function can return a reference. But the object must remain in scope after the function has ended. In the following code, the returned reference would be invalid because the referenced object was a stack variable that died long ago. Some compilers will compile this code without a warning, but that is not as per the C++ standard. Your code might run in simple programs like the one shown here, if you are accessing the variable before the memory location is overwritten. This is due to sheer luck. Don't ignore the warning.

int& fx()
{
    int x = 17;
 
    int& t = x;
 
    return t;
}
 
int main()
{
    // k is invalid because
    // it references an object
    // that is dead long ago
    int& k = fx();
 
    return 0;
}

The following code has no problems because the returned reference is to an int that is in scope. But nobody writes this code in practice because the input argument is being returned back as a reference, which is redundant.

int& fx(int& x)
{
    int& t = x;
 
    return t;
}
 
int main()
{
    int y = 87;
 
    // k is a reference to y only
    int& k = fx(y);
 
    return 0;
}

The following code is correct as far as returning a reference is concerned, because you are returning a reference to an object that has been created on the heap, and therefore continues to stay there even after fx has exited without deleting the allocation. This code, though seemingly correct, will lead to a memory leak, because there is no call to delete for releasing the heap allocation.

int& fx()
{
    int *i = new int;
 
    *i = 99;
 
    return *i;
}
 
int main()
{
    int y = fx();
 
    cout << y << endl;
 
    return 0;
}

So how to release the heap memory? You might think that the following code will release the heap allocation. But it won't compile.

int& fx()
{
    int *i = new int;
 
    *i = 99;
 
    return *i;
}
 
int main()
{
    int y = fx();
 
    cout << y << endl;
 
    // COMPILER ERROR
    delete y;
 
    return 0;
}

The following code shows the right way. As you must have seen by now that returning by reference is not that easy as it might seem. It can lead to bugs and memory leaks, if not carefully done.

int& fx()
{
    int *i = new int;
 
    *i = 99;
 
    return *i;
}
 
int main()
{
    int& y = fx();
 
    cout << y << endl;
 
    delete &y;
 
    return 0;
}

Is a return by reference of any use at all? You might be surprised that it is one of the most used features, and we have been using it almost daily, un-knowingly. It is mostly used during operator overloading, for example, during "chaining" the cout and cin that we have been using till today. The code - cout << "A" - displays A to the monitor, and returns a reference to cout which can be chained to the next insertion operator - cout << "A" << 88 << x << endl;

const References

A constant reference is declared with the const keyword. The referenced object cannot be altered through a const reference. The following code gives an error because x is a const reference and it cannot be altered.

int main()
{
    int i = 99;
 
    const int& x = i;
 
    // COMPILER ERROR
    x++;
 
    return 0;
}

Un-necessary copies can be avoided by passing function arguments as const references instead of by value. For a detailed discussion please refer the video for this tutorial.

Classroom Training

Classroom training in C/C++ is available at our training institute. Click here for details. You can also register yourself here.

Video Tutorial

We have created a video tutorial that can be downloaded and watched offline on your windows based computer. It is in the form of an exe file. No installation is required, it runs by double clicking the file. Since the exe file is hosted on the Google Drive, it should be very safe because Google itself checks for any virus or malware. The video can be watched offline, no internet is required.

Pricing

This single video will cost you USD $5. When you download the video, you will be able to watch the first few minutes for free, as a sample. The video app will prompt you for payment through PayPal and other payment options. If you want ALL VIDEOS, NOT JUST THIS ONE then go here: Complete Set of C/C++ Video Tutorials The entire set is priced at USD $50.

Screenshots

This is a screenshot of the software that should give you a good idea of the available functionality. Click on the image to see a larger view.screenshot of the video being played in the software

Installation

This software doesn't require any installation. Just download and double-click to run it. It doesn't require administrative priviliges to run. It runs in limited access mode, so should be very safe.

supports windows xp and later Supported Operating Systems

These videos can be run on 32-bit as well as 64-bit versions of the Microsoft Windows Operating Systems. Windows XP and all later OS are supported. If you want to run it on Windows XP and Windows Vista, then you must also have the .NET Framework Version 3.5 installed on your machines. Those users who have Windows 7 and later donot have to worry because these operating systems already have the .NET Framework installed on them.

Download Links

IMPORTANT NOTE: This download contains ONLY ONE video. The video is about the topic of this tutorial.

Want ALL Videos? If you want ALL VIDEOS, NOT JUST THIS ONE then go here: Complete Set of C/C++ Video Tutorials. TIP: If you plan to buy these videos, then it would be more economical to buy the entire set.

DISCLAIMER: Even though every care has been taken in making this software bug-free, but still please take a proper backup of your PC data before you use it. We shall not be responsible for any damages or consequential damages arising out of the use of this software. Please use it solely at your own risk.

Please download the software here.
download the video Functions and References



Creative Commons License
This Blog Post/Article "Functions and References" by Parveen (Hoven) is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
Updated on 2020-02-07. Published on: 2015-10-05