TutorialsC++References
Share:

References

intermediate

Part of Object-Oriented C++

Theory

References are one of C++'s most powerful features. A reference is an alias — an alternative name for an existing variable. Unlike pointers, references are safer and easier to use because they cannot be null and cannot be reassigned.

What is a Reference?

A reference is declared using the & symbol:

int original = 42;
int& ref = original;  // ref is a reference to original
 
ref = 100;  // Modifies original
cout << original;  // 100

After declaration, ref and original are two names for the same memory location. Any change through one is visible through the other.

References vs Pointers

| Feature | Reference | Pointer | |---------|-----------|---------| | Syntax | int& ref = x; | int* ptr = &x; | | Nullable | No (must be initialized) | Yes (can be nullptr) | | Reassignable | No (bound for life) | Yes | | Access members | ref.member | ptr->member or (*ptr).member | | Memory overhead | None (alias) | 8 bytes (stores address) |

When to use references:

  • Function parameters (pass by reference)
  • Return values from functions
  • Operator overloading
  • Range-based for loops

When to use pointers:

  • Nullable parameters/returns
  • Dynamic memory allocation
  • Arrays
  • Data structures (linked lists, trees)

Reference as Function Parameters (Pass by Reference)

Passing by reference avoids copying and allows the function to modify the original:

// Pass by value — copies the argument
void swapByValue(int a, int b) {
    int temp = a;
    a = b;
    b = temp;  // Original unaffected
}
 
// Pass by reference — modifies the original
void swapByReference(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;  // Original modified!
}
 
int x = 5, y = 10;
swapByReference(x, y);
cout << x << " " << y;  // 10 5

Benefits of pass-by-reference:

  • No copying — more efficient for large objects
  • Modification — can change the argument
  • Cleaner syntax than pointers (no dereferencing)

Reference as Return Value

Returning a reference allows the function result to be used on the left side of an assignment:

class Array {
private:
    int data[100];
 
public:
    int& operator[](int index) {
        return data[index];  // Returns reference
    }
};
 
Array arr;
arr[0] = 42;  // Works because operator[] returns a reference
cout << arr[0];  // 42

Warning: Never return a reference to a local variable!

int& badFunction() {
    int local = 42;
    return local;  // BUG! local is destroyed when function returns
}  // Dangling reference!

Const References

const references prevent modification and can bind to temporaries:

void printData(const std::vector<int>& data) {
    // data is read-only — cannot modify
    for (int v : data) {
        cout << v << " ";
    }
}
 
// Can bind to temporaries (rvalues)
const std::string& ref = "Hello";  // OK
// std::string& ref = "Hello";  // Error! Cannot bind non-const ref to temporary

Use const references for function parameters when:

  • You don't need to modify the argument
  • The object is large (expensive to copy)
  • You want to accept both lvalues and rvalues

Lvalue vs Rvalue References (Brief)

Lvalues have a memory address and persist beyond a single expression. Rvalues are temporary values that don't have a persistent address.

int x = 10;     // x is an lvalue
int y = x + 5;  // x + 5 is an rvalue (temporary)
 
int& lref = x;         // lvalue reference
// int& lref2 = x + 5; // Error! Cannot bind lvalue ref to rvalue
 
int&& rref = x + 5;    // rvalue reference (C++11)

Move Semantics (Brief)

Rvalue references enable move semantics — transferring resources from a temporary object instead of copying:

class MyString {
private:
    char* data;
 
public:
    // Move constructor — steals resources from temporary
    MyString(MyString&& other) noexcept
        : data(other.data) {
        other.data = nullptr;  // Leave other in valid state
    }
 
    // Move assignment
    MyString& operator=(MyString&& other) noexcept {
        if (this != &other) {
            delete[] data;       // Release current resources
            data = other.data;   // Steal resources
            other.data = nullptr;
        }
        return *this;
    }
};

Move semantics are automatic with the STL containers — when you return a std::vector from a function, the compiler moves it instead of copying.

When in doubt, pass parameters by const& for read-only access and by reference & when you need to modify the argument. Pass small types (int, char, bool) by value.

Practical Examples

Example 1: Pass by Reference in Practice
cpp
Example 2: Const References and Lifetime Extension
cpp

Use const auto& in range-based for loops when you don't need to modify elements. Use auto& when you do need to modify them. Use auto (by value) only for small, cheap-to-copy types.

Exercises

Pass by Reference vs Value

easy

Write a program that demonstrates the difference between pass-by-value and pass-by-reference. Create a function incrementByValue and incrementByReference. Show that the value version doesn't affect the original but the reference version does.

Expected Output:

Before: 5\nInside incrementByValue: 6\nAfter incrementByValue: 5\nInside incrementByReference: 6\nAfter incrementByReference: 6

Find Min and Max with References

medium

Write a function findMinMax that takes an array of integers and uses reference parameters to return both the minimum and maximum values.

Expected Output:

Minimum: 3\nMaximum: 91

Implement a Reference-Based Stack

hard

Implement a simple Stack class that uses a fixed-size array internally. The push() and top() methods should return references so that top() can be used on the left side of an assignment. Include proper const overloads.

Expected Output:

Top: 99\n99 10

Mini Quiz

Mini Quiz

Mini Project

Mini Project: Matrix Class with Reference Semantics

Create a Matrix class that uses references and operator overloading to provide intuitive access to elements. Implement row and column access via references, enabling syntax like matrix(1, 2) = 5.0.

Requirements:

    Bonus Challenge

    Implement move semantics for the Matrix class. Add a transpose() method that returns a new matrix.