References
A reference is another name for an existing variable. Unlike a pointer, it can't be null, can't be re-pointed, and needs no special syntax to use. References are how C++ functions modify their arguments and avoid copying large objects.
Declaring a reference
#include <iostream> int main() { int score = 70; int& alias = score; // alias refers to score alias += 5; // changes score std::cout << score << ' ' << alias << '\n'; score = 100; std::cout << alias << '\n'; std::cout << std::boolalpha << (&alias == &score) << '\n'; // same address }
75 75 100 true
int& reads "reference to int". A reference must be initialised when it's declared, and it then refers to that same object for its whole life — assigning to it assigns to the object.
References as parameters
A reference parameter lets a function change the caller's variable:
#include <iostream> #include <string> void swap_values(int& a, int& b) { int tmp = a; a = b; b = tmp; } void make_title(std::string& s) { if (!s.empty() && s[0] >= 'a' && s[0] <= 'z') s[0] = static_cast<char>(s[0] - 32); } int main() { int x = 3, y = 8; swap_values(x, y); std::string name = "grace"; make_title(name); std::cout << x << ' ' << y << ' ' << name << '\n'; }
8 3 Grace
(The standard library already provides std::swap.) Compare this with C, where you'd pass pointers and write *a everywhere.
const references: read without copying
Passing a std::string or std::vector by value copies all its contents. A const reference gives the function read-only access to the original — no copy:
#include <iostream> #include <string> #include <vector> double average(const std::vector<double>& values) { double sum = 0; for (double v : values) sum += v; return values.empty() ? 0 : sum / values.size(); } int main() { std::vector<double> readings(1'000'000, 2.5); // a million values std::cout << average(readings) << '\n'; }
2.5
Trying to modify through a const reference is a compile error — the function's signature tells the caller their data is safe.
#include <string> void shout(const std::string& s) { s += "!"; } int main() { shout("hello"); }
main.cpp:4:7: error: no viable overloaded '+='
A const reference can also bind to a temporary value, such as the string literal above or the result of an expression; a non-const reference can't.
References in loops
As lesson 11 showed, for (auto& x : v) modifies elements and for (const auto& x : v) reads them without copying.
Returning references
A function can return a reference to something that outlives the call — such as an element of a container passed in:
#include <iostream> #include <vector> int& largest(std::vector<int>& values) { int* best = &values[0]; for (int& v : values) { if (v > *best) best = &v; } return *best; } int main() { std::vector<int> scores{70, 95, 88}; largest(scores) = 100; // assign through the returned reference for (int s : scores) std::cout << s << ' '; std::cout << '\n'; }
70 100 88
Never return a reference to a local variable: it's destroyed when the function returns, leaving a dangling reference. Compilers warn:
#include <string> const std::string& greeting() { std::string text = "hello"; return text; } int main() { return static_cast<int>(greeting().size()); }
main.cpp:5:12: warning: reference to stack memory associated with local variable 'text' returned [-Wreturn-stack-address]
References vs. pointers
Reference T& | Pointer T* | |
|---|---|---|
| Can be null | No | Yes (nullptr) |
| Can refer to something else later | No | Yes |
| Syntax to use | Like a normal variable | *p, p->member |
| Use for | Parameters, range-for, returning access to existing objects | Optional references, re-pointing, low-level code (lesson 27) |
Try it yourself
Write a function that normalises a vector of scores in place so the highest becomes 100.
80 60 100 90