cpp
references
01_references.cpp⚙️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 = # // 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
*/