Understanding References in C++

Hello, young coders! Let’s step into the wonderful world of C++ and explore a super cool feature – “references”. Don’t worry if this sounds like a mystery right now. We’ll uncover it step by step. Ready? Let’s dive in!

Understanding References in C++

Introduction

Imagine you’ve taken a fantastic selfie that you want to share with your friends. Instead of physically giving them your phone, you can simply show them the photo. In C++, references work similarly. They provide a way to work with data without directly manipulating it. This article explores the concept of references, why we need them, and how they can be used in our programs. By understanding references, you can efficiently work with data and pass it around without making unnecessary copies. So, let’s dive into the world of references and unlock their power!

What is Reference in C++

When you declare a variable as a reference, it’s like giving it another name that points to the same data. You declare a variable as a reference by adding an ‘&’ symbol in its declaration.

Additionally, a reference variable is a type of variable that acts like a pointer to another variable. The ‘&’ symbol is used to indicate the memory address of a variable. Variables linked with reference variables can be accessed using either their original names or the reference variable names that point to them.

Syntax:

C++
datatype &reference_variable = original_variable;

Here’s a breakdown of the syntax components:

  • datatype: This is the data type of the variable being referenced.
  • &: This symbol indicates that a reference variable is being declared.
  • reference_variable: This is the name you give to the reference variable.
  • original_variable: This is the variable whose value the reference variable will refer to.

Code Example:

C++
#include <iostream>
using namespace std;

int main() {
    int num = 10;
    int &ref = num; // Creating a reference named 'ref' to the variable 'num'

    cout << "Original num: " << num << endl;
    cout << "Reference ref: " << ref << endl;

    ref = 20; // Changing the value of 'num' indirectly through the reference 'ref'

    cout << "Updated num: " << num << endl;

    return 0;
}

Output:

C++
Original num: 10
Reference ref: 10
Updated num: 20

Explanation:

  • Reference Creation: The code creates a reference named ‘ref’.
  • Indirect Association: The reference ‘ref’ is associated with the variable ‘num’.
  • Shared Value: Any modification to ‘ref’ also affects the value of ‘num’.
  • Value Display: The original values of ‘num’ and ‘ref’ are displayed.
  • Change Impact: Changing ‘ref’ indirectly alters the value of ‘num’, as demonstrated in the output.

Applications of Reference in C++

References in C++ have various practical uses, some of which are outlined below:

  1. Modify Function Parameters: References can be used in functions to modify the values of parameters directly, making it easier to update variables outside the function.
  2. Avoid Copying Large Structures: When passing large data structures to functions, using references can help avoid unnecessary copying, which improves performance.
  3. Modify All Objects in a Loop: References can be employed in a “for each” loop to directly modify each object in a collection, streamlining code and enhancing efficiency.
  4. Avoid Copying Objects in a Loop: By using references in a “for each” loop, the need to create copies of objects is eliminated, leading to better performance and memory usage.

Modify Function Parameters in C++

When you pass variables to a function in C++, you can choose whether you want the function to be able to modify the original value or just use a copy of it. This depends on how you pass these variables – by value or by reference.

  1. Passing by Value: When you pass a parameter by value, the function works with a copy of that variable. Any changes made inside the function won’t affect the original variable.
  2. Passing by Reference: When you pass a parameter by reference, the function works directly with the original variable. This means that any changes made inside the function will reflect on the original variable.

Let’s see an example of both:

C++
#include <iostream>
using namespace std;

// Function to demonstrate passing by value
void modifyByValue(int num) {
    num = num * 2;
    cout << "Inside modifyByValue: " << num << endl;
}

// Function to demonstrate passing by reference
void modifyByReference(int &num) {
    num = num * 2;
    cout << "Inside modifyByReference: " << num << endl;
}

int main() {
    int a = 5;
    int b = 5;

    modifyByValue(a);
    cout << "Outside modifyByValue (original value): " << a << endl;

    modifyByReference(b);
    cout << "Outside modifyByReference (modified value): " << b << endl;

    return 0;
}

Output:

C++
Inside modifyByValue: 10
Outside modifyByValue (original value): 5
Inside modifyByReference: 10
Outside modifyByReference (modified value): 10

As you can see:

  • In ‘modifyByValue‘, we changed the value of the parameter, but the original variable a remained the same because we passed by value.
  • In ‘modifyByReference‘, the original variable ‘b‘ got modified because we passed it by reference (as indicated by the ‘&‘ symbol in the function parameter).

Avoid Copying Large Structures

Think of a structure in C++ like a big box of toys. Now, if you wanted to share these toys with a friend, it would be inefficient and time-consuming to duplicate every single toy and give them an entirely new box with those duplicated toys. Instead, you’d probably just point to your original box and say, “Here it is!” Similarly, in C++, copying large structures means creating entirely new memory allocations for the copied data. This can be slow and wasteful.

To avoid this, we use references or pointers. Instead of giving an entirely new box of toys (or copying the structure), we simply share the address or “point” to the original box (structure).

Code Example:

C++
#include<iostream>
using namespace std;

struct LargeData {
    int data[1000];  // Just an example; it has 1000 integer elements
};

void processWithoutCopying(const LargeData &dataset) {
    // Do something with the dataset, e.g., print the first element
    cout << "First element: " << dataset.data[0] << endl;
}

int main() {
    LargeData bigData;
    bigData.data[0] = 42;  // Assign a value to the first element

    processWithoutCopying(bigData);  // We pass a reference, not a copy

    return 0;
}

Output:

C++
First element: 42

Explanation:

  • The function “processWithoutCopying” accepts a reference to the “LargeData” structure using the ‘&’ symbol.
  • This reference allows direct access to the original “bigData” without duplicating it.
  • By using a reference, the function operates on the actual data, which is more efficient.
  • This approach avoids the overhead of creating and managing a duplicate copy.
  • Overall, it optimizes memory usage and enhances performance when dealing with large datasets.

Modify All Objects in a Loop in C++:

Imagine you have a box of colored pencils, and each pencil has a different color. Now, you decide to paint each pencil with a glitter finish. In programming, this box of pencils can be represented as an array or a vector, and our action of painting each pencil with a glitter finish can be likened to modifying each element in that array or vector.

Code Example:

C++
#include <iostream>
#include <vector>

int main() {
    // Define a vector of integers
    std::vector<int> numbers = {1, 2, 3, 4, 5};

    // Display original numbers
    std::cout << "Original numbers: ";
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    // Modify each number in the vector
    for (int &num : numbers) {  // Note the '&' - this means we're accessing a reference to the actual element.
        num += 1;
    }

    // Display modified numbers
    std::cout << "Modified numbers: ";
    for (int num : numbers) {
        std::cout << num << " ";
    }

    return 0;
}

Output:

C++
Original numbers: 1 2 3 4 5 
Modified numbers: 2 3 4 5 6 

Explanation:

  • The “&” symbol in the loop “for (int &num : numbers)” creates a reference to each actual number in the vector.
  • Modifying “num” within the loop directly changes the corresponding number in the vector.
  • Using “&” ensures changes made to “num” are reflected in the original vector.
  • Without “&”, modifications would only affect a copy of the number, not the vector itself.
  • Employing “&” preserves changes and updates the vector with the modified values.

Avoiding Copying Objects in a Loop in C++

When we use loops in C++, especially with containers like vectors or lists, a common mistake is to inadvertently copy objects during each iteration. This can slow down the program, as copying can be expensive, especially for large objects.

Why is this an issue?

  1. Performance: Copying large objects repeatedly can decrease performance.
  2. Unexpected behavior: If you modify the copied object in the loop, the original object remains unchanged.

Solution: Use references!

By using references, instead of copying the object, you’re simply referring to the original object. Any changes made will affect the original object, and there’s no performance hit from copying.

Example:

Let’s consider a simple example with a ‘vector‘ of ‘string‘ objects:

Using Copies (Not Ideal):

C++
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<string> fruits = {"apple", "banana", "cherry"};

    for (string fruit : fruits) {
        fruit = "fruit";
    }

    for (string fruit : fruits) {
        cout << fruit << endl;
    }

    return 0;
}

Output:

C++
apple
banana
cherry

Notice how the fruits’ names didn’t change even though we tried to change them in the loop? That’s because we were modifying copies of the original strings.

Using References (Recommended):

C++
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<string> fruits = {"apple", "banana", "cherry"};

    for (string& fruit : fruits) { // Notice the '&' which makes 'fruit' a reference
        fruit = "fruit";
    }

    for (string fruit : fruits) {
        cout << fruit << endl;
    }

    return 0;
}

Output:

C++
fruit
fruit
fruit

Now, as we used references, the original strings in the vector were modified.

In Summary: When working with loops, especially with containers, it’s a good practice to use references to avoid unnecessary copying. This ensures better performance and avoids unexpected behaviors.

Some Examples

Example 1: Changing Numbers Inside Functions

We can use nicknames inside functions to change the original numbers.

C++
#include <iostream>
using namespace std;

// Function that changes a number using reference
void changeNumber(int &num) {
    num += 10;
}

int main() {
    int number = 5;

    cout << "Before calling the function: " << number << endl;
    changeNumber(number);
    cout << "After calling the function: " << number << endl;

    return 0;
}

Output:

C++
Before calling the function: 5
After calling the function: 15

Explanation:

  • The function ‘changeNumber‘ is designed to modify a number using a reference.
  • When the function is called with the variable ‘number‘, it directly alters its value by adding 10.
  • Initially, the value of ‘number‘ is 5.
  • After calling the function, the value of ‘number‘ becomes 15.

Example 2: Getting Back References from Functions

C++
#include <iostream>
using namespace std;

// Function that takes two integer references
// as parameters and updates their values
void modifyValues(int &a, int &b) {
    a *= 2;
    b += 10;
}

int main() {
    int x = 5, y = 20;

    cout << "Original values: x = " << x << ", y = " << y << endl;

    // Calling the function with references
    modifyValues(x, y);

    cout << "Updated values: x = " << x << ", y = " << y << endl;

    return 0;
}

Output:

C++
Original values: x = 5, y = 20
Updated values: x = 10, y = 30

Explanation:

  • The function ‘modifyValues‘ takes two integer references (int &a and int &b) as parameters.
  • When this function is called with variables x and y, it modifies the values of x and y using the references.
  • As a result, the changes made to x and y inside the function are reflected in the main function where they were initially declared.

Example: A Reference to a Reference

C++
#include <iostream>
using namespace std;

int main() {
    int num = 42;
    int &ref1 = num;  // Reference to num
    int &ref2 = ref1; // Reference to ref1

    cout << "num: " << num << endl;      // Output: num: 42
    cout << "ref1: " << ref1 << endl;    // Output: ref1: 42
    cout << "ref2: " << ref2 << endl;    // Output: ref2: 42

    ref2 = 100; // Modifying through ref2

    cout << "num after modification: " << num << endl;   // Output: num after modification: 100
    cout << "ref1 after modification: " << ref1 << endl; // Output: ref1 after modification: 100
    cout << "ref2 after modification: " << ref2 << endl; // Output: ref2 after modification: 100

    return 0;
}

Output:

C++
num: 42
ref1: 42
ref2: 42
num after modification: 100
ref1 after modification: 100
ref2 after modification: 100

Explanation:

  • An integer variable ‘num‘ is declared with a value of 42.
  • Two references are created: ‘ref1‘ is a reference to num, and ‘ref2‘ is a reference to ‘ref1‘.
  • Modifying ‘ref2‘ also changes ‘ref1‘ and, subsequently, modifies the original num variable.
  • The output illustrates how modifications through ‘ref2‘ affect num and ‘ref1‘.

Advantages and Disadvantages of References

AdvantagesDisadvantages
Efficient and direct accessCannot be uninitialized or null
Avoids unnecessary copyingRequires an existing object to refer to
Simplifies function argumentsLimited flexibility in reassignment
Can modify the referenced object directlyNo ownership semantics
The Good and Not-so-good About References

Main Points to Remember

  • References are just nicknames for variables.
  • They let us use data without changing the actual stuff.
  • They’re great for functions that need to change their arguments.
  • Using references wisely can make our code cleaner and faster.

Conclusion

And that’s a wrap! You’ve taken your first steps into the world of references in C++, seen how they work in real code, and even learned about their role in something advanced like multiple inheritance. Try creating your own examples and using references in your projects. Keep practicing, and you’ll soon be a reference pro! Happy coding!

FAQs

  • What are references in C++?
    They’re simply nicknames for variables.
  • Why do we use references in C++?
    They let us work with the original data without changing it.
  • How do I create a reference in C++?
    Use the ‘&’ operator, like ‘int &y = x;’.
  • Can functions return references in C++?
    Yes, functions can return references, allowing us to modify the original variable.
  • Are there downsides to using references in C++?
    Yes, although references can speed up our programs, they can also make our code harder to understand if we’re not careful.
Deepak Vishwakarma

Founder

RELATED Articles

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.