Skip to content

References

References are aliases for other variables. This chapter covers references, their relationship to pointers, and object lifetime management.

┌─────────────────────────────────────────────────────────────┐
│ References vs Pointers │
├─────────────────────────────────────────────────────────────┤
│ │
│ Reference Pointer │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ int& ref = │ │ int* ptr = │ │
│ │ var; │ │ &var; │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ • Always bound to an object • Can be null │
│ • Cannot be reseated • Can be reassigned │
│ • No arithmetic • Supports arithmetic │
│ • Syntax like regular variable • Needs * to dereference│
│ │
└─────────────────────────────────────────────────────────────┘
int value = 42;
int& ref = value; // ref is alias for value
ref = 100; // Changes value to 100
std::cout << value; // 100
// Reference is not a copy
std::cout << &value << std::endl; // Same address
std::cout << &ref << std::endl; // Same address
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 5, y = 10;
swap(x, y); // x=10, y=5
}
std::vector<int> vec = {1, 2, 3, 4, 5};
int& getElement(std::vector<int>& v, size_t index) {
return v[index]; // Returns reference to element
}
int main() {
getElement(vec, 0) = 100; // Modify through reference
std::cout << vec[0]; // 100
}

Warning: Don’t return reference to local variable!

int& badFunction() {
int x = 5;
return x; // DANGER: x is destroyed when function returns
}
void printValue(const int& value) {
// value = 100; // Error: can't modify
std::cout << value << "\n";
}
int main() {
int x = 42;
printValue(x); // Pass by const reference
// Can also pass temporaries
printValue(100); // Temporary is kept alive for the call
}
int x = 10;
int& lref = x; // Lvalue reference - binds to named variable
// int& lref2 = 10; // Error: can't bind to rvalue
int x = 10;
int&& rref = 10; // Rvalue reference - binds to rvalue (temporary)
int&& rref2 = x; // Error: can't bind lvalue to rvalue ref
// Move semantics use rvalue references
int&& moveRef = std::move(x); // Cast to rvalue
int value = 42;
int* ptr = &value;
int*& refToPtr = ptr; // Reference to pointer
refToPtr = nullptr; // Changes ptr to null
int* const& refToConstPtr = ptr; // Const reference to pointer
void function() {
int x; // Destroyed when function returns
std::string s; // Destructor called
}
void function() {
static int count = 0; // Lives for entire program
count++;
}
void function() {
int* p = new int(42); // Lives until explicitly deleted
delete p; // Must manually delete
}

RAII (Resource Acquisition Is Initialization)

Section titled “RAII (Resource Acquisition Is Initialization)”
class File {
FILE* file;
public:
File(const char* name) {
file = fopen(name, "r");
}
~File() {
if (file) fclose(file); // Automatic cleanup
}
};
void function() {
File f("data.txt"); // Opens file
// Use file...
} // File automatically closed

Temporary objects are extended to match reference lifetime:

const std::string& getName() {
return "Temporary"; // Temporary is extended
}
std::string&& getRvalue() {
return std::string("Temporary"); // Must use rvalue ref
}
#include <iostream>
#include <functional>
void increment(int& value) {
value++;
}
void decrement(int& value) {
value--;
}
int main() {
int num = 10;
// Using std::ref to pass by reference through std::function
std::function<void()> inc = std::bind(increment, std::ref(num));
std::function<void()> dec = std::bind(decrement, std::ref(num));
inc(); // num = 11
inc(); // num = 12
dec(); // num = 11
std::cout << num << "\n"; // 11
return 0;
}
  • References are aliases - not new variables
  • References must be initialized
  • References cannot be null
  • Use const references to avoid copies
  • Rvalue references enable move semantics
  • RAII ensures automatic resource cleanup

Let’s learn about smart pointers - the modern way to manage dynamic memory.

Next Chapter: 22_smart_pointers.md - Smart Pointers (unique_ptr, shared_ptr)