C++ course menu
C++ References: Aliases, const References and Reference Parameters
Understand C++ references: what an lvalue reference is, how it aliases a variable, const references, references in range-based for loops and function parameters, returning references safely, and references vs pointers.
On this page
A reference is another name for an existing variable. Once created, the reference and the original are the same object: reading or changing one reads or changes the other. You have already used references in function parameters; this lesson explains exactly how they work.
Creating a reference#
Add & after the type:
#include <iostream>
int main() {
int score = 80;
int& alias = score; // alias is another name for score
alias += 5;
std::cout << score << ' ' << alias << '\n';
score = 100;
std::cout << alias << '\n';
}85 85
100There is only one integer in memory here, with two names.
Rules for references#
- A reference must be initialized when it is created:
int& r;does not compile. - A reference cannot be re-seated: it refers to the same variable for its whole life. Assigning to it changes the value of that variable; it does not make it refer to something else.
- There are no "null references": a reference always refers to an object (unless you create a dangling one by mistake).
#include <iostream>
int main() {
int a = 1;
int b = 2;
int& r = a;
r = b; // copies b's value INTO a; r still refers to a
b = 50;
std::cout << a << ' ' << b << ' ' << r << '\n';
}2 50 2const references#
A const reference gives read-only access. It is the standard way to look at a large object without copying it:
#include <iostream>
#include <string>
int main() {
std::string title = "Learn C++ Step by Step";
const std::string& view = title;
std::cout << view.size() << '\n';
// view += "!"; // error: view is read-only
title += "!"; // the original can still change
std::cout << view << '\n';
}22
Learn C++ Step by Step!A const reference can also bind to a temporary value, such as a literal or the result of an expression, and keeps that temporary alive as long as the reference exists: const std::string& s = std::string("hi") + "!"; is valid.
References as function parameters#
This is where references are used most:
#include <iostream>
#include <string>
#include <vector>
void add_item(std::vector<std::string>& cart, const std::string& item) {
cart.push_back(item);
}
void print_cart(const std::vector<std::string>& cart) {
for (const auto& item : cart) std::cout << "- " << item << '\n';
}
int main() {
std::vector<std::string> cart;
add_item(cart, "Milk");
add_item(cart, "Bread");
print_cart(cart);
}- Milk
- Breadstd::vector<std::string>& cartletsadd_itemchange the caller's vector.const std::string& itemandconst std::vector<...>& cartavoid copies and promise not to change anything.
References in loops#
The same idea applies in a range-based for:
#include <iostream>
#include <vector>
int main() {
std::vector<int> stock = {5, 0, 12};
for (int& s : stock) {
s += 10; // changes the real elements
}
for (const int& s : stock) {
std::cout << s << ' ';
}
std::cout << '\n';
}15 10 22Returning references#
A function can return a reference, which lets the caller read or modify an element inside an object that outlives the call:
#include <iostream>
#include <vector>
int& largest(std::vector<int>& values) {
std::size_t best = 0;
for (std::size_t i = 1; i < values.size(); ++i) {
if (values[i] > values[best]) best = i;
}
return values[best]; // refers into the caller's vector: safe
}
int main() {
std::vector<int> v = {4, 19, 7};
largest(v) = 0; // set the largest element to 0
for (int x : v) std::cout << x << ' ';
std::cout << '\n';
}4 0 7This is safe because values belongs to the caller. Returning a reference to a local variable is never safe, because the local is destroyed when the function returns (see Scope and Lifetime).
References vs pointers#
C++ also has pointers, which hold the address of a variable. They overlap with references, but differ in important ways:
Prefer references when something must always refer to a valid object (most function parameters). Use pointers when "nothing" is a valid option, or when you need to change what is referred to. The next lesson covers Pointers.
Common mistakes#
Practice#
Practice exercise
Write void normalize(std::vector<double>& marks, double max_mark) that converts every mark to a percentage of max_mark in place. Normalize {18, 25, 9.5} out of 25 and print the result.
Show solution
#include <iostream>
#include <vector>
void normalize(std::vector<double>& marks, double max_mark) {
for (double& m : marks) {
m = m / max_mark * 100;
}
}
int main() {
std::vector<double> marks = {18, 25, 9.5};
normalize(marks, 25);
for (double m : marks) std::cout << m << ' ';
std::cout << '\n';
}72 100 38Key takeaway#
Finished this lesson?