C++ References

Creating aliases for variables

🔗 What are C++ References?

C++ references are aliases for existing variables, providing an alternative name for the same memory location. They enable efficient parameter passing and cleaner syntax than pointers.


// Simple reference example
int x = 10;
int& ref = x;  // ref is now an alias for x
                                    

Reference Concepts

🏷️

Alias

References are alternative names

int num = 5;
int& alias = num;

Function Parameters

Pass by reference for efficiency

void modify(int& x) {
    x = x * 2;
}
↩️

Return References

Functions can return references

int& getElement(int arr[], int i) {
    return arr[i];
}
🛡️

Const References

const references grant read-only access to variables, preventing modification while avoiding copies. Declared as const T&, they are ideal for passing large objects to functions that only need to inspect data, such as print or compare operations. const references can bind to temporaries and literals, enhancing flexibility. This practice improves performance and safety, ensuring the function doesn’t accidentally alter the original data. Always prefer const references over plain references for input-only parameters.

void display(const int& x) {
    cout << x;  // Can't modify x
}

🔹 Basic Reference Example

References in C++ provide an alias—another name—for an existing variable. Declared with & after the type (e.g., int& ref = var), a reference must be initialized immediately and cannot be reassigned to refer to a different variable. Unlike pointers, references are safer and syntactically cleaner, as they don’t require dereferencing. They are commonly used in function parameters to avoid copying and in range-based for loops to modify elements directly.

#include <iostream>
using namespace std;

int main() {
    int original = 42;
    int& reference = original;  // Create reference
    
    cout << "Original value: " << original << endl;
    cout << "Reference value: " << reference << endl;
    
    // Modifying through reference
    reference = 100;
    
    cout << "After modifying reference:" << endl;
    cout << "Original value: " << original << endl;
    cout << "Reference value: " << reference << endl;
    
    // Both have same memory address
    cout << "Original address: " << &original << endl;
    cout << "Reference address: " << &reference << endl;
    
    return 0;
}

Output:

Original value: 42
Reference value: 42
After modifying reference:
Original value: 100
Reference value: 100
Original address: 0x7fff5fbff6ac
Reference address: 0x7fff5fbff6ac

🔹 Pass by Reference

Pass by reference allows functions to directly modify the original variable using the & symbol. This avoids copying, improves performance for large objects, and enables functions to alter arguments, as seen in swap operations. References maintain a direct link to the original data, making them ideal for mutating inputs, returning multiple values, or handling complex types efficiently. However, care is needed to avoid unintended changes, and const references can be used for read-only access to large data.

#include <iostream>
using namespace std;

// Pass by value (creates copy)
void passByValue(int x) {
    x = x * 2;
    cout << "Inside passByValue: " << x << endl;
}

// Pass by reference (modifies original)
void passByReference(int& x) {
    x = x * 2;
    cout << "Inside passByReference: " << x << endl;
}

int main() {
    int num1 = 10;
    int num2 = 10;
    
    cout << "Before function calls:" << endl;
    cout << "num1: " << num1 << ", num2: " << num2 << endl;
    
    passByValue(num1);
    passByReference(num2);
    
    cout << "After function calls:" << endl;
    cout << "num1: " << num1 << ", num2: " << num2 << endl;
    
    return 0;
}

Output:

Before function calls:
num1: 10, num2: 10
Inside passByValue: 20
Inside passByReference: 20
After function calls:
num1: 10, num2: 20

🔹 Const References

const references grant read-only access to variables, preventing modification while avoiding copies. Declared as const T&, they are ideal for passing large objects to functions that only need to inspect data, such as print or compare operations. const references can bind to temporaries and literals, enhancing flexibility. This practice improves performance and safety, ensuring the function doesn’t accidentally alter the original data. Always prefer const references over plain references for input-only parameters.

#include <iostream>
#include <string>
using namespace std;

// Efficient: no copying, read-only access
void displayInfo(const string& name, const int& age) {
    cout << "Name: " << name << endl;
    cout << "Age: " << age << endl;
    // name = "New Name";  // Error! Cannot modify const reference
}

// Function that returns a const reference
const string& getLongerString(const string& str1, const string& str2) {
    return (str1.length() > str2.length()) ? str1 : str2;
}

int main() {
    string firstName = "Alice";
    string lastName = "Johnson";
    int userAge = 25;
    
    displayInfo(firstName, userAge);
    
    const string& longer = getLongerString(firstName, lastName);
    cout << "Longer string: " << longer << endl;
    
    return 0;
}

Output:

Name: Alice
Age: 25
Longer string: Johnson

🔹 Reference vs Pointer Comparison

References and pointers both enable indirect access but differ in syntax, safety, and use cases. A reference is an alias that must be bound at initialization and cannot be null or reassigned, making it safer. A pointer is a variable holding an address; it can be null, reassigned, and supports arithmetic. References are typically used for function parameters and return values, while pointers are needed for dynamic memory, arrays, and optional parameters. Understanding these differences is crucial for writing robust C++ code.

#include <iostream>
using namespace std;

int main() {
    int value = 42;
    
    // Reference
    int& ref = value;        // Must be initialized
    ref = 100;               // Changes the original value
    
    // Pointer
    int* ptr = &value       // Can be declared without initialization
    *ptr = 200;              // Dereference to change value
    
    cout << "Value: " << value << endl;
    cout << "Reference: " << ref << endl;
    cout << "Pointer value: " << *ptr << endl;
    
    // Key differences:
    // 1. References cannot be null
    // 2. References cannot be reassigned
    // 3. References don't need dereferencing
    // 4. No pointer arithmetic with references
    
    int another = 50;
    // ref = another;        // This assigns value, not reassigns reference
    ptr = &another          // This reassigns pointer to new address
    
    cout << "After reassignment:" << endl;
    cout << "Reference: " << ref << endl;
    cout << "Pointer value: " << *ptr << endl;
    
    return 0;
}

Output:

Value: 200
Reference: 200
Pointer value: 200
After reassignment:
Reference: 50
Pointer value: 50

🧠 Test Your Knowledge

What symbol is used to declare a reference in C++?