cpp

references

01_references.cpp⚙️
/**
 * ============================================================
 * C++ REFERENCES
 * ============================================================
 * 
 * This file covers:
 * - What are references
 * - Reference declaration
 * - References vs pointers
 * - Reference parameters
 * - Const references
 * - Rvalue references (C++11)
 * 
 * Compile: g++ -std=c++17 -Wall 01_references.cpp -o references
 * Run: ./references
 * 
 * ============================================================
 */

#include <iostream>
#include <string>
#include <vector>

using namespace std;

// Function declarations
void incrementByPointer(int* ptr);
void incrementByReference(int& ref);
void printLarge(const string& str);
int& getElement(vector<int>& vec, int index);

int main() {
    cout << "============================================" << endl;
    cout << "     C++ REFERENCES" << endl;
    cout << "============================================" << endl << endl;

    // ========================================================
    // PART 1: WHAT IS A REFERENCE?
    // ========================================================
    
    cout << "--- PART 1: WHAT IS A REFERENCE? ---" << endl << endl;
    
    /*
     * A REFERENCE is an ALIAS for an existing variable.
     * - Once initialized, it cannot be changed to refer to something else
     * - Must be initialized when declared
     * - Cannot be null
     * - No extra memory (typically) - just another name
     */
    
    int x = 10;
    int& ref = x;  // ref is now an alias for x
    
    cout << "int x = 10;" << endl;
    cout << "int& ref = x;  // ref is an alias for x" << endl << endl;
    
    cout << "x   = " << x << endl;
    cout << "ref = " << ref << " (same value)" << endl;
    cout << "&x   = " << &x << endl;
    cout << "&ref = " << &ref << " (same address!)" << endl;
    
    // Modifying through reference affects original
    cout << "\nModifying through reference:" << endl;
    ref = 50;
    cout << "ref = 50;" << endl;
    cout << "x   = " << x << " (x changed!)" << endl;
    
    x = 100;
    cout << "\nx = 100;" << endl;
    cout << "ref = " << ref << " (ref reflects change)" << endl;
    
    cout << endl;

    // ========================================================
    // PART 2: REFERENCE RULES
    // ========================================================
    
    cout << "--- PART 2: REFERENCE RULES ---" << endl << endl;
    
    // Rule 1: Must be initialized
    int val = 5;
    int& r1 = val;    // OK
    // int& r2;       // ERROR: must be initialized
    
    // Rule 2: Cannot be null
    // int& r3 = nullptr;  // ERROR: references can't be null
    
    // Rule 3: Cannot be reassigned to another variable
    int a = 10, b = 20;
    int& refA = a;
    cout << "int a = 10, b = 20;" << endl;
    cout << "int& refA = a;" << endl;
    cout << "refA = " << refA << endl;
    
    refA = b;  // This COPIES b's value to a, doesn't reassign reference!
    cout << "\nrefA = b;  // Copies b to a, NOT reassignment!" << endl;
    cout << "a    = " << a << " (now has b's value)" << endl;
    cout << "refA = " << refA << endl;
    cout << "&refA = " << &refA << " (still points to a)" << endl;
    cout << "&a    = " << &a << endl;
    
    cout << endl;

    // ========================================================
    // PART 3: REFERENCES VS POINTERS
    // ========================================================
    
    cout << "--- PART 3: REFERENCES VS POINTERS ---" << endl << endl;
    
    int num = 42;
    int& numRef = num;    // Reference
    int* numPtr = &num;   // Pointer
    
    cout << "Access:" << endl;
    cout << "  Through reference: " << numRef << " (direct)" << endl;
    cout << "  Through pointer: " << *numPtr << " (need *)" << endl;
    
    cout << "\nModification:" << endl;
    numRef = 100;   // Direct
    cout << "  numRef = 100 → num is " << num << endl;
    *numPtr = 200;  // Need dereference
    cout << "  *numPtr = 200 → num is " << num << endl;
    
    cout << "\nComparison Table:" << endl;
    cout << "┌───────────────────┬─────────────┬─────────────┐" << endl;
    cout << "│ Feature           │ Reference   │ Pointer     │" << endl;
    cout << "├───────────────────┼─────────────┼─────────────┤" << endl;
    cout << "│ Must initialize   │ Yes         │ No          │" << endl;
    cout << "│ Can be null       │ No          │ Yes         │" << endl;
    cout << "│ Can reassign      │ No          │ Yes         │" << endl;
    cout << "│ Syntax to access  │ Direct      │ Dereference │" << endl;
    cout << "│ Memory overhead   │ Usually none│ Size of ptr │" << endl;
    cout << "│ Arithmetic        │ No          │ Yes         │" << endl;
    cout << "└───────────────────┴─────────────┴─────────────┘" << endl;
    
    cout << endl;

    // ========================================================
    // PART 4: REFERENCE PARAMETERS
    // ========================================================
    
    cout << "--- PART 4: REFERENCE PARAMETERS ---" << endl << endl;
    
    int value = 10;
    
    cout << "Original value: " << value << endl;
    
    // Using pointer (C style)
    incrementByPointer(&value);
    cout << "After incrementByPointer: " << value << endl;
    
    // Using reference (C++ style - cleaner!)
    incrementByReference(value);
    cout << "After incrementByReference: " << value << endl;
    
    // Swap example
    int m = 5, n = 10;
    cout << "\nBefore swap: m=" << m << ", n=" << n << endl;
    
    // Inline swap using references
    int& rm = m;
    int& rn = n;
    int temp = rm;
    rm = rn;
    rn = temp;
    
    cout << "After swap: m=" << m << ", n=" << n << endl;
    
    cout << endl;

    // ========================================================
    // PART 5: CONST REFERENCES
    // ========================================================
    
    cout << "--- PART 5: CONST REFERENCES ---" << endl << endl;
    
    /*
     * const reference: Can read but not modify
     * - Commonly used for function parameters
     * - Efficient: no copy
     * - Safe: can't accidentally modify
     * - Can bind to temporaries!
     */
    
    int original = 42;
    const int& constRef = original;
    
    cout << "const int& constRef = original;" << endl;
    cout << "constRef = " << constRef << endl;
    // constRef = 100;  // ERROR: cannot modify through const reference
    
    original = 100;  // Can still modify original directly
    cout << "original = 100;" << endl;
    cout << "constRef = " << constRef << " (reflects change)" << endl;
    
    // Const reference can bind to temporaries
    const int& tempRef = 42;  // Extends lifetime of temporary
    cout << "\nconst int& tempRef = 42;  // OK for const ref" << endl;
    cout << "tempRef = " << tempRef << endl;
    
    // int& badRef = 42;  // ERROR: non-const ref can't bind to temporary
    
    // Efficient parameter passing
    string longString = "This is a very long string...";
    printLarge(longString);  // No copy made!
    printLarge("Temporary string");  // Works with temporaries too!
    
    cout << endl;

    // ========================================================
    // PART 6: REFERENCE RETURN VALUES
    // ========================================================
    
    cout << "--- PART 6: REFERENCE RETURNS ---" << endl << endl;
    
    vector<int> vec = {10, 20, 30, 40, 50};
    
    cout << "Original vector: ";
    for (int v : vec) cout << v << " ";
    cout << endl;
    
    // Get reference to element and modify it
    getElement(vec, 2) = 999;  // Modify element at index 2
    
    cout << "After getElement(vec, 2) = 999: ";
    for (int v : vec) cout << v << " ";
    cout << endl;
    
    // ⚠️ WARNING: Never return reference to local variable!
    // int& badFunction() {
    //     int local = 10;
    //     return local;  // DANGER: local destroyed after function returns
    // }
    
    cout << endl;

    // ========================================================
    // PART 7: RVALUE REFERENCES (C++11)
    // ========================================================
    
    cout << "--- PART 7: RVALUE REFERENCES (C++11) ---" << endl << endl;
    
    /*
     * Lvalue: Has an address (can appear on left of =)
     * Rvalue: Temporary, no persistent address
     * 
     * lvalue reference: int&
     * rvalue reference: int&&
     * 
     * Rvalue references enable:
     * - Move semantics (avoid copies)
     * - Perfect forwarding
     */
    
    int lval = 10;           // lval is an lvalue
    int& lref = lval;        // lvalue reference
    // int& badLref = 10;    // ERROR: can't bind lvalue ref to rvalue
    
    int&& rref = 10;         // rvalue reference binds to temporary
    int&& rref2 = lval + 5;  // Expression result is rvalue
    
    cout << "int lval = 10;       // lvalue" << endl;
    cout << "int& lref = lval;    // lvalue reference" << endl;
    cout << "int&& rref = 10;     // rvalue reference" << endl;
    cout << "int&& rref2 = lval + 5;  // rvalue reference" << endl;
    
    cout << "\nlref = " << lref << endl;
    cout << "rref = " << rref << endl;
    cout << "rref2 = " << rref2 << endl;
    
    // Can modify through rvalue reference
    rref = 100;
    cout << "\nAfter rref = 100: " << rref << endl;
    
    cout << "\n💡 Rvalue references are mainly used for:" << endl;
    cout << "   - Move constructors" << endl;
    cout << "   - Move assignment operators" << endl;
    cout << "   - Perfect forwarding in templates" << endl;
    
    cout << endl;

    // ========================================================
    // PART 8: BEST PRACTICES
    // ========================================================
    
    cout << "--- PART 8: BEST PRACTICES ---" << endl << endl;
    
    cout << "When to use references:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "• Function parameters (avoid copying)" << endl;
    cout << "• Return values (access container elements)" << endl;
    cout << "• Range-based for loops" << endl;
    cout << "• When null is not a valid option" << endl;
    
    cout << "\nWhen to use pointers:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "• Dynamic memory allocation" << endl;
    cout << "• Optional parameters (can be null)" << endl;
    cout << "• Pointer arithmetic needed" << endl;
    cout << "• Need to reassign to different objects" << endl;
    
    cout << "\nReference parameter guidelines:" << endl;
    cout << "─────────────────────────────────────────" << endl;
    cout << "• Use const T& for input parameters" << endl;
    cout << "• Use T& for output/inout parameters" << endl;
    cout << "• Use T&& for move semantics" << endl;
    
    cout << endl;

    cout << "============================================" << endl;
    cout << "REFERENCES SUMMARY:" << endl;
    cout << "============================================" << endl;
    cout << "• Reference = alias for existing variable" << endl;
    cout << "• Must initialize, cannot be null" << endl;
    cout << "• Cannot be reassigned" << endl;
    cout << "• Use const& for efficient parameters" << endl;
    cout << "• && for rvalue references (move)" << endl;
    cout << "============================================" << endl;

    return 0;
}

// ============================================================
// FUNCTION DEFINITIONS
// ============================================================

void incrementByPointer(int* ptr) {
    if (ptr) {
        (*ptr)++;
    }
}

void incrementByReference(int& ref) {
    ref++;  // Much cleaner syntax!
}

void printLarge(const string& str) {
    cout << "String: " << str << " (no copy made!)" << endl;
}

int& getElement(vector<int>& vec, int index) {
    return vec[index];  // Return reference to element
}

// ============================================================
// EXERCISES:
// ============================================================
/*
 * 1. Write a function that swaps two strings using references
 * 
 * 2. Write a function that returns the larger of two integers
 *    by reference (so it can be modified)
 * 
 * 3. Create a function that takes a vector by reference and
 *    removes all negative numbers
 * 
 * 4. Implement a simple string class with:
 *    - Reference returning operator[]
 *    - Const reference for reading
 * 
 * 5. Experiment with move semantics:
 *    - Create a class with move constructor
 *    - Use std::move to transfer ownership
 */
References - C++ Tutorial | DeepML