References and const references
Understand references as aliases, pass values efficiently, and compare references with pointers.
Learning objectives
- Explain a reference as an alias for an existing object.
- Pass an object by reference to allow a function to modify it.
- Use a const reference for read-only access.
- Compare references and pointers.
Prerequisites: functions, variables, pointers, and object lifetime at a basic level. Review the pointer lesson if address and dereference syntax is unfamiliar.
What is a reference?
A reference is another name for an existing object. It is declared with & after the type and must be initialized when it is created.
int score{10};
int& alias{score};
alias = 25;
// score is now 25The reference does not create a second independent integer. Both names refer to the same object.
Pass by value: function gets a copy
void add_one(int value) {
value += 1;
}
int main() {
int score{10};
add_one(score);
// score is still 10
}The parameter value is a separate object initialized from the argument. Changing it does not change the caller's score.
Pass by reference: function works with caller's object
void add_one(int& value) {
value += 1;
}
int main() {
int score{10};
add_one(score);
// score is now 11
}The int& parameter is a reference to the caller's integer. The assignment changes the original object.
Pass by const reference: read without copying
#include <iostream>
#include <string>
void greet(const std::string& name) {
std::cout << "Hello, " << name << '\n';
// name += "!"; // error: read-only through this reference
}A const std::string& allows the function to read the caller's string without making a string copy, and prevents this function from modifying it through that parameter. This is a common interface for read-only access to larger objects.
Reference and pointer comparison
| Reference | Pointer |
|---|---|
| Must be initialized | Can be initialized to nullptr |
| Acts as an alias to an object | Stores an address |
| Cannot be reseated to refer to another object | Can be assigned a different address |
| Used directly like the referred object | Must be dereferenced with * to access the pointed-to object |
Important lifetime rule
A reference must not outlive the object it refers to. Returning a reference to a local variable is a bug because that local object is destroyed when the function returns.
int& broken() {
int local{5};
return local; // WRONG: reference dangles after return
}Return by value for ordinary small results. Modern C++ can efficiently return objects by value, so do not use references just to avoid every copy.
Common mistakes
- Declaring a reference without initializing it.
- Expecting a reference to be reseated by assigning another object to it.
- Returning a reference to a local variable.
- Using a non-const reference when the function should only read its argument.
- Assuming pass-by-reference automatically transfers ownership. It does not.
Practice
- Write
void reset(int& value)that sets the caller's integer to zero. - Write a function that prints a
const std::string&without modifying it. - Predict the values after one pass-by-value call and one pass-by-reference call.
- Explain why returning a reference to a local variable is unsafe.
Practice solutions
Show worked solutions
void reset(int& value) { value = 0; }The reference parameter aliases the caller's integer.#include <iostream> #include <string> void print_text(const std::string& text) { std::cout << text << '\n'; }The const reference avoids a string copy and prevents modification through this parameter.- A pass-by-value function changes only its local copy. A pass-by-reference function can change the caller's original object. Trace the assignments in each function to see which object is modified.
- A local automatic object is destroyed when its function returns. A reference to it would then refer to an object whose lifetime has ended; using that reference is undefined behavior.
Check your understanding
Does int& r = x; create a second integer?
Answer: No. It creates a reference name for the existing object x.
Summary
Use references when a function needs an existing object, use non-const references when it should modify that object, and use const references for read-only access. Never let a reference outlive the object it refers to.