Skip to main content

References

A reference is an alias - another name for an existing object. Unlike pointers, references cannot be null, must be initialized, and cannot be reseated.

Reference = Alias

Not a new variable, just another name for the same memory location.

int x = 42;
int& ref = x; // ref IS x, not a copy
ref = 100; // modifies x

Basic References​

References create an alternate name for an existing variable. All operations on the reference affect the original variable because they're the same thing.

int x = 42;
int& ref = x; // ref is an alias for x

ref = 100; // Modifies x
std::cout << x; // 100
std::cout << ref; // 100
std::cout << &ref; // Same address as &x

x = 200; // Modifying x
std::cout << ref; // 200 (ref sees the change)

Initialization Rules​

// âś… Must initialize
int x = 42;
int& ref = x;

// ❌ Cannot be uninitialized
int& bad_ref; // Error

// ❌ Cannot be null
int& null_ref = nullptr; // Error

// ❌ Cannot reseat
int y = 100;
ref = y; // Copies value, doesn't reseat

Function Parameters​

References enable efficient pass-by-reference semantics, allowing functions to modify caller's variables without copying.

// Pass by value (copy)
void increment_copy(int x) {
x++; // Modifies copy
}

// Pass by reference (no copy)
void increment_ref(int& x) {
x++; // Modifies original
}

int value = 10;
increment_copy(value); // value still 10
increment_ref(value); // value now 11

const References​

Const references allow reading but prevent modification, making them ideal for passing large objects efficiently without allowing changes.

Best Practice for Large Objects
// ❌ Expensive copy
void print(std::string s) {
std::cout << s;
}

// âś… No copy, can't modify
void print(const std::string& s) {
std::cout << s;
// s[0] = 'X'; // Error: const
}

print("Hello"); // âś… Binds to temporary

Why const&?

  • No copy overhead (efficient)
  • Can bind to temporaries
  • Documents intent (won't modify)
  • Prevents accidental changes

Return by Reference​

Functions can return references to allow modification of class members or avoid copying large objects.

class Widget {
std::string name;
public:
// Non-const getter (allows modification)
std::string& getName() {
return name;
}

// const getter (read-only)
const std::string& getName() const {
return name;
}
};

Widget w;
w.getName() = "New Name"; // âś… Modifies through reference

const Widget cw;
// cw.getName() = "X"; // ❌ const version returns const&

Returning non-const references enables chaining and direct modification of internals. Returning const references provides efficient read access without allowing modification. This pattern is common for container element access (vector::operator[]) and getter methods.

Danger: Dangling References​

Never return references to local variables. Locals are destroyed when the function returns, creating dangling references.

int& dangerous() {
int x = 42;
return x; // ❌ x destroyed at return
}

int& safe(int& param) {
return param; // âś… param outlives function
}

class Safe {
int data;
public:
int& get() { return data; } // âś… member outlives call
};

References don't extend object lifetimes. Returning a reference to a local creates the same problem as returning a pointer to a local - undefined behavior.

L‑values and R‑values (C++11)​

For a more detailed explanation, see: Value categories

Move Semantics​

Move operations transfer ownership of resources from a source object to a destination object, avoiding unnecessary and expensive copying.

For a more detailed explanation, see: Move and copy semantics

Reference Wrappers​

std::reference_wrapper allows storing references in containers, which normally can't hold references directly.

int x = 10, y = 20, z = 30;

// ❌ Can't store references in vector
// std::vector<int&> refs; // Error

// âś… Can store reference_wrappers
std::vector<std::reference_wrapper<int>> refs;
refs.push_back(std::ref(x));
refs.push_back(std::ref(y));
refs.push_back(std::ref(z));

refs[0].get()++; // Increments x
std::cout << x; // 11

References can't be stored in containers because they're not regular objects (can't be reassigned, no default construction). std::reference_wrapper wraps a reference in a copyable, assignable object that acts like a reference.

References vs Pointers​

FeatureReferencePointer
Syntaxint& ref = xint* ptr = &x
Null possible❌ No✅ Yes (nullptr)
Must initialize✅ Yes❌ No (but should)
Can reassign❌ No (always same object)✅ Yes
DereferenceAutomaticManual (*ptr)
SizeSame as original8 bytes (64-bit)

Summary​

References vs Pointers
  • Reference = alias (same object)
  • Pointer = variable storing address
  • References safer (no null, no reassignment)
  • Pointers more flexible (nullable, reassignable)
Key rules
  • Must initialize at declaration
  • Cannot be null or reseated
  • Assignment copies values, doesn't reseat
  • Perfect for function parameters
Usage patterns
  • const T& for read-only parameters (large objects)
  • T& for modifiable parameters
  • Return T& for member access
  • Never return reference to local