---
title: References
source: 'https://academia.sh/en/courses/cpp-fundamentals/references'
course: 'C++ Fundamentals and Object Design'
language: en
updated: '2026-09-26T03:02:45+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - "Measures that a reference parameter works with the caller's own object"
    - 'Applies the choice among value, reference, and const reference to when each one is used'
    - 'Distinguishes that a const reference can bind to a temporary object, while an ordinary reference cannot'
---

# References

Measures that a reference parameter works with the caller's own object, producing no copy at all.

The previous lesson measured that an object takes its own memory when it is copied. This
lesson measures the exact opposite question: if a function takes an object **by
reference**, is any copy produced at all, or does the function work directly with the
caller's own object? The measurement kind is **self**: when `p` is given to the function
`nudge(Point& n)`, the `n` inside the function and the `p` in the caller are the same
object — `&p == &n` is true, and no copying step happens between them.

In C, the only way to pass a struct to a function "by reference" was to pass a pointer:
`nudge(&p)`, accessed inside the function with `(*ptr).x` or `ptr->x`. This forced the
`&` operator to be written by hand at the call site, and forced a dereference (`*`) or
the arrow operator to be written at every access inside the function — and there was
always a possibility that `ptr` was `nullptr`. In C++ a reference removes both problems
at once: `nudge(p)` is written at the call site, no `&` is needed; `n.x` is written
inside the function, no dereference is needed; and because a reference cannot be left
uninitialized, the `nullptr` case is ruled out by the language itself.

## What a Reference Is

A **reference** is a second name given to an existing object — it is not a new object,
and it does not get a new memory address. When `Point& r = p;` is written, `r` is `p`
itself; every read and write made through `r` reaches `p`'s memory directly. This is
different from a pointer (`Point* ptr = &p;`): a pointer is a separate object, has its
own memory, and **holds** `p`'s address; a reference, on the other hand, is the **same**
thing as `p` itself, and occupies no separate memory location (the compiler often turns
a reference into a pointer in the generated code, but in the language's own model a
reference is not a new object).

The practical consequence of this distinction is this: a pointer does not guarantee,
before `ptr->x` is written, that it points at a valid object — it may need to be checked
by hand (`if (ptr != nullptr)`). There is no such check for a reference, and none is
needed — the language assumes that a reference is always bound to a valid object (code
that deliberately breaks this assumption, for example by dereferencing an invalid
pointer and binding it to a reference, produces undefined behavior, but no code within
this lesson's scope does anything like that). This is why a function signature that
takes a reference requires less defensive code than one that takes a pointer — the
existence of the parameter is accepted from the start.

```cpp
// point.hpp — a simple struct for measuring passage by reference
#ifndef POINT_HPP
#define POINT_HPP

struct Point {
    int x;
    int y;
};

#endif
```

## A Struct Passed by Reference

```cpp
// reference-measurement.cpp — does nudge(Point&) work with the caller's own object
#include <cstdio>

#include "point.hpp"

void nudge(Point& n, Point* caller_address) {
    std::printf("is n inside nudge the same object as the caller %s\n",
                &n == caller_address ? "yes" : "no");
    n.x += 10;
    n.y += 10;
}

int main() {
    Point p{1, 2};

    nudge(p, &p);

    std::printf("after nudge p.x=%d p.y=%d\n", p.x, p.y);
    return 0;
}
```

```text
is n inside nudge the same object as the caller yes
after nudge p.x=11 p.y=12
```

**CR12.** `nudge(Point& n, Point* caller_address)` takes its argument by reference; when
`nudge(p, &p)` is called, `n` is `p` **itself** — `&n == caller_address` (that is,
`&n == &p`) is true. Although the line `n.x += 10;` writes through `n`, because `n` and
`p` are the same object this write directly changes `p.x`; after `nudge` returns, `p.x`
and `p.y` are already updated (`11` and `12`) — `main` continues with the caller's
object changed, without receiving any return value and without performing any
assignment. This is the exact opposite of the previous lesson's **copy** kind: there,
`b` was a different address from `a`; here, `n` is the **same** address as `p` — no
copying step runs at all, because passing a reference argument never calls the copy
constructor.

## Value, Reference, const Reference: Which One, When

A function has three options when it takes an object as an argument, and each one
encodes a different intent:

- **By value** (`Point n`): the function works with its own copy, and never affects the
  caller's object in any way. This suits small, cheaply copied types (an `int`, a
  `Point` of two `int`s); for large or expensively copied types (a class managing its
  own memory, like `Buffer` in the third lesson), it produces a needless copy on every
  call.
- **By reference** (`Point& n`): the function works directly with the caller's object,
  and can **modify** it. `nudge` does this — its signature tells the reader "this
  function can modify the object you give it."
- **By const reference** (`const Point& n`): the function accesses the caller's object
  without copying it, but **cannot modify it** — `n.x += 10;` would be a compile error
  here. This encodes the intent "read a large object cheaply, but do not modify it";
  this is exactly why the copy constructor's and copy assignment's argument in the
  previous lessons was `const Buffer&`.

The intent is understood by reading the signature: `void write(const Point& n)` only
reads the caller's object; `void nudge(Point& n)` modifies it; `void copy(Point n)`
never touches it at all, and works on its own copy.

There is no shortcut among these three options like "always use a reference, it is
faster." Taking a small type that fits in a single machine word, like `int`, by
reference generally gains nothing — passing a reference also generally means (depending
on the code the compiler produces) passing an address, and an address is typically the
same size as an `int`; moreover, because a function taking a reference can now modify
the caller's object, even a read-only use is left open to an accidental write (this risk
is closed by `const int&`, but then no gain remains either). Taking small types by value
is both simpler and, in most cases, equally fast. The real gain from reference and const
reference appears for types that are expensive to copy — classes that allocate their own
memory, like `Buffer` in the third and fifth lessons, or large structs with many
members.

## Reference Parameters and Return Values

A reference can be used not only for parameters but also for return types. A function
like `Point& first(Point array[])` can return the array's first element **as itself,
not as a copy** — the caller can write `first(d) = Point{0, 0};` and directly change the
array's first element, because the return value is `d[0]` itself. This lesson does not
run a reference return value (this is the typical usage of overloaded operators such as
`operator[]`, and is covered in K05), but it rests on the same principle as parameter
references: returning a reference is granting access to an existing object without
constructing a new one.

## Reference Arguments in Member Functions

This principle works the same way for member functions too. The fifth lesson's
`Buffer::operator=` signature was `Buffer& operator=(const Buffer& other)` — the `other`
parameter here, too, is the caller's own object; when `b = a;` is written, `other`
inside `operator=` is the same address as `a`. Every line where `operator=` reads
`other.data_` reads `a`'s memory directly; if `other` had been taken by value (as
`Buffer other`), every `b = a;` call would first build a `Buffer` copy, and `operator=`
would work with that copy — meaning both a needless copy constructor call and a
needless destructor call. The choice of `const Buffer&` carries the same intent here
too: read `a` without copying it, do not modify it.

## A Reference Cannot Be Rebound

Once a pointer is set, it can be changed to point at a different object:
`ptr = &another;`. This is not possible for a reference — after `Point& r = p;` is
written, `r = another;` is not a rebinding; it **assigns** `another`'s value onto `r`
(that is, onto `p`). Which object a reference is bound to is fixed the moment the
reference is created, and this binding never changes for the reference's lifetime —
this is why a reference must be initialized **at the moment it is created**; there is
no such thing as an empty reference (unlike a pointer's `nullptr`).

## Binding a const Reference to a Temporary

```cpp
// temporary-binding-measurement.cpp — can a const reference bind to a temporary
#include <cstdio>

#include "point.hpp"

void write(const Point& n) {
    std::printf("inside write x=%d y=%d\n", n.x, n.y);
}

int main() {
    Point p{1, 2};
    write(p);

    const Point& r = Point{3, 4};
    std::printf("via the temporary x=%d y=%d\n", r.x, r.y);

    write(Point{5, 6});
    return 0;
}
```

```text
inside write x=1 y=2
via the temporary x=3 y=4
inside write x=5 y=6
```

**CR13.** The line `const Point& r = Point{3, 4};` binds a `const` reference to a
**temporary** object (`Point{3, 4}`, an unnamed value constructed in the middle of the
expression). Normally a temporary's lifetime ends at the end of the expression that
constructs it, but when it is bound to a `const` reference the C++ standard **extends**
that temporary's lifetime to match the reference's lifetime — as long as `r` is valid,
`Point{3, 4}` is also valid, so reading `r.x` and `r.y` is not undefined behavior. If
the same binding had been written as `Point& r = Point{3, 4};` (without const), it
would not compile: an ordinary reference cannot bind to a temporary, because allowing a
write through the temporary (like `r.x = 99;`) would mean modifying an object the
caller could never see, one that is about to disappear immediately — the language rules
this out from the start as meaningless. The call `write(Point{5, 6})` uses the same
rule: because `write`'s parameter is `const Point&`, the temporary can bind directly;
if the parameter were an ordinary `Point&`, this call would not compile.

## References and Memory Layout

M11/K01 covered a struct's memory layout — member order, alignment, and padding. A
reference does not establish a "new memory layout" in that lesson's sense — when
`Point& r = p;` is written, `r` has no `sizeof` value or memory layout of its own,
because `r` is not a new object, it is another name for `p`. A class with a reference
member (a member like `Point& reference_;`) may look like an exception to this rule —
such a member typically occupies the space of a pointer, because the compiler turns it
into code that way — but this is an implementation detail; the language itself does not
define "where" a reference is kept, only which object it is bound to.

## The Bounding Measurement

The expression this lesson measures is of kind **self** — but only in some cases. A
parameter taking `const Point&` can be either **self** or **temporary**; the signature
itself does not say which one, only the argument at the call site determines it.

```cpp
// self-or-temporary.cpp — which kind does a const reference parameter fall under, in each case
#include <cstdio>

#include "point.hpp"

void write(const Point& n) {
    std::printf("inside write x=%d y=%d\n", n.x, n.y);
}

int main() {
    Point p{7, 8};

    write(p);
    write(Point{9, 10});

    std::printf("did p stay unchanged %s\n", (p.x == 7 && p.y == 8) ? "yes" : "no");
    return 0;
}
```

```text
inside write x=7 y=8
inside write x=9 y=10
did p stay unchanged yes
```

**CR14.** In the call `write(p)`, `n` is `p` itself — kind **self**; in the call
`write(Point{9, 10})`, `n` is bound to an unnamed temporary constructed in the middle
of the expression — kind **temporary**. Both calls use the same signature
(`const Point&`) and call the same function, but which kind the parameter falls under
is determined only by the argument itself at the call site. Being `const` prevents the
function from writing through `n` in both cases — this is why `p` remains unchanged on
the third line; whether `write`'s `n` is self or temporary has no effect on `p`'s state
at all. It is not possible to give a definite answer to "is this parameter self" by
looking at a function signature alone — the signature only answers the question **can
it be modified** (reference or const reference); the answer to whether it is self or
temporary is determined anew at each call.

This limit produces a consequence worth considering when designing a class: seeing that
a function accepts a `const Point&` parameter does not guarantee that the function will
always be called with a persistent object. The function body must assume that the
reference it receives may have a lifetime that ends at the close of the call
expression — for example, storing `n`'s address somewhere and using it after the
function returns (something like `Point* p = &n; return p;`) produces a dangling
pointer if `n` is bound to a temporary. This lesson does not run such code; it only
states the limit. The reason the next lesson's `operator+` returns a **new object**
rather than a reference comes from the same limit: the sum the operator produces is a
local value constructed inside the operator's own body, and if it needs to live on
after the function returns, it must be returned by value, not by reference.

## Summary

- A reference is a second name given to an existing object; it does not construct a new
  object, and does not take a new address.
- An argument taken by reference is the caller's **self** — no copying step runs,
  `&n == &p` is true.
- The choice among value, reference, and const reference encodes intent:
  copy-and-do-not-touch, modify, or read-without-copying-but-do-not-modify.
- Once a reference is created, it cannot be rebound to a different object.
- A `const` reference can bind to a temporary (the temporary's lifetime is extended);
  an ordinary reference cannot.
- A parameter taking `const Point&` can be either **self** or **temporary** depending
  on the call; the signature alone does not say which.

## Next Step

This lesson measured that a reference works directly with the caller's object, without
copying; the next and final lesson measures why an operator (`operator+`) returns a new
**temporary**, and why `operator+=` instead returns `*this` by reference.
