References
intermediatePart of Object-Oriented C++
Theory
References are one of C++'s most powerful features. A reference is an alias — an alternative name for an existing variable. Unlike pointers, references are safer and easier to use because they cannot be null and cannot be reassigned.
What is a Reference?
A reference is declared using the & symbol:
int original = 42;
int& ref = original; // ref is a reference to original
ref = 100; // Modifies original
cout << original; // 100After declaration, ref and original are two names for the same memory location. Any change through one is visible through the other.
References vs Pointers
| Feature | Reference | Pointer |
|---------|-----------|---------|
| Syntax | int& ref = x; | int* ptr = &x; |
| Nullable | No (must be initialized) | Yes (can be nullptr) |
| Reassignable | No (bound for life) | Yes |
| Access members | ref.member | ptr->member or (*ptr).member |
| Memory overhead | None (alias) | 8 bytes (stores address) |
When to use references:
- Function parameters (pass by reference)
- Return values from functions
- Operator overloading
- Range-based for loops
When to use pointers:
- Nullable parameters/returns
- Dynamic memory allocation
- Arrays
- Data structures (linked lists, trees)
Reference as Function Parameters (Pass by Reference)
Passing by reference avoids copying and allows the function to modify the original:
// Pass by value — copies the argument
void swapByValue(int a, int b) {
int temp = a;
a = b;
b = temp; // Original unaffected
}
// Pass by reference — modifies the original
void swapByReference(int& a, int& b) {
int temp = a;
a = b;
b = temp; // Original modified!
}
int x = 5, y = 10;
swapByReference(x, y);
cout << x << " " << y; // 10 5Benefits of pass-by-reference:
- No copying — more efficient for large objects
- Modification — can change the argument
- Cleaner syntax than pointers (no dereferencing)
Reference as Return Value
Returning a reference allows the function result to be used on the left side of an assignment:
class Array {
private:
int data[100];
public:
int& operator[](int index) {
return data[index]; // Returns reference
}
};
Array arr;
arr[0] = 42; // Works because operator[] returns a reference
cout << arr[0]; // 42Warning: Never return a reference to a local variable!
int& badFunction() {
int local = 42;
return local; // BUG! local is destroyed when function returns
} // Dangling reference!Const References
const references prevent modification and can bind to temporaries:
void printData(const std::vector<int>& data) {
// data is read-only — cannot modify
for (int v : data) {
cout << v << " ";
}
}
// Can bind to temporaries (rvalues)
const std::string& ref = "Hello"; // OK
// std::string& ref = "Hello"; // Error! Cannot bind non-const ref to temporaryUse const references for function parameters when:
- You don't need to modify the argument
- The object is large (expensive to copy)
- You want to accept both lvalues and rvalues
Lvalue vs Rvalue References (Brief)
Lvalues have a memory address and persist beyond a single expression. Rvalues are temporary values that don't have a persistent address.
int x = 10; // x is an lvalue
int y = x + 5; // x + 5 is an rvalue (temporary)
int& lref = x; // lvalue reference
// int& lref2 = x + 5; // Error! Cannot bind lvalue ref to rvalue
int&& rref = x + 5; // rvalue reference (C++11)Move Semantics (Brief)
Rvalue references enable move semantics — transferring resources from a temporary object instead of copying:
class MyString {
private:
char* data;
public:
// Move constructor — steals resources from temporary
MyString(MyString&& other) noexcept
: data(other.data) {
other.data = nullptr; // Leave other in valid state
}
// Move assignment
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data; // Release current resources
data = other.data; // Steal resources
other.data = nullptr;
}
return *this;
}
};Move semantics are automatic with the STL containers — when you return a std::vector from a function, the compiler moves it instead of copying.
When in doubt, pass parameters by const& for read-only access and by reference & when you need to modify the argument. Pass small types (int, char, bool) by value.
Practical Examples
Use const auto& in range-based for loops when you don't need to modify elements. Use auto& when you do need to modify them. Use auto (by value) only for small, cheap-to-copy types.
Exercises
Pass by Reference vs Value
Write a program that demonstrates the difference between pass-by-value and pass-by-reference. Create a function incrementByValue and incrementByReference. Show that the value version doesn't affect the original but the reference version does.
Expected Output:
Before: 5\nInside incrementByValue: 6\nAfter incrementByValue: 5\nInside incrementByReference: 6\nAfter incrementByReference: 6Find Min and Max with References
Write a function findMinMax that takes an array of integers and uses reference parameters to return both the minimum and maximum values.
Expected Output:
Minimum: 3\nMaximum: 91Implement a Reference-Based Stack
Implement a simple Stack class that uses a fixed-size array internally. The push() and top() methods should return references so that top() can be used on the left side of an assignment. Include proper const overloads.
Expected Output:
Top: 99\n99 10Mini Quiz
Mini Quiz
Mini Project
Mini Project: Matrix Class with Reference Semantics
Create a Matrix class that uses references and operator overloading to provide intuitive access to elements. Implement row and column access via references, enabling syntax like matrix(1, 2) = 5.0.
Requirements:
Bonus Challenge
Implement move semantics for the Matrix class. Add a transpose() method that returns a new matrix.