---
title: 'Constructors and Destructors'
source: 'https://academia.sh/en/courses/cpp-fundamentals/constructors-and-destructors'
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 an argument built through an implicit type conversion is a temporary object'
    - 'Applies how the explicit keyword prevents implicit construction'
    - 'Shows the order of the member initializer list and the constructor chain'
---

# Constructors and Destructors

Measures that an argument implicitly built from an int is an unnamed, temporary object, and measures its lifetime with a constructor/destructor pair.

The previous lesson measured which object a member function works with, through `this`
— but that object always already existed. This lesson asks the same question at the
**moment an object comes to exist**: when is a **constructor** (a member function
carrying the same name as the class, run once while the object is being built) called,
and when does a **destructor** (a member function prefixed with `~`, run once while the
object is being destroyed) run? This time the measurement kind is **temporary**: it will
track the lifetime of an unnamed object, implicitly built from an argument of a function
call.

## Constructor and Destructor

A constructor carries the same name as the class, has no return type (not even `void`
is written), and is called by the compiler while an object is being built. A destructor
takes the form `~ClassName()`, takes no arguments, and is called by the compiler when an
object reaches the end of its lifetime — the programmer never calls it by hand. If a
class writes no constructor at all, the compiler generates a **default constructor**
(one taking no arguments, building each data member with its own default value); once a
constructor is written, this generation stops.

A class can have more than one constructor — as long as their parameter count or type
differs, they are distinguished by the same overload rules as any function. A class can,
by contrast, have only **one** destructor; because a destructor takes no arguments, it
cannot be overloaded.

The biggest difference between a constructor and an ordinary member function is that,
while the constructor is running, the object is **not yet complete**. While
`Quantity(int value)` runs, `value_` is built by the member initializer list's
`value_(value)`; once the body begins running, `value_` already holds a valid value and
can be read inside the body, but until the body runs, the object's other members (this
class has only `value_`, but in a class with more members, the members that follow) may
not yet be built. For this reason, calling another member function through `this` inside
a constructor's body — especially a virtual member function, a topic outside this
course's scope — carries the risk of reaching a not-yet-fully-built state of the object;
this lesson makes no such call.

The destructor's position is symmetric: while the destructor is running, the object is
still complete and valid, but once the destructor returns the object is considered gone
— the memory for its member variables may still physically remain, but the program
accessing it is undefined behavior (a rule this course inherits from M11/K02: undefined
behavior is never run, it is only mentioned in prose). `Quantity::~Quantity`'s body reads
and prints `value_` one last time; no code after that line should access the same
object's `value_`.

```cpp
// quantity.hpp — a constructor, a destructor, and a constructor chain
#ifndef QUANTITY_HPP
#define QUANTITY_HPP

#include <cstdio>

class Quantity {
public:
    Quantity(int value) : value_(value) {
        std::printf("constructor ran value=%d\n", value_);
    }

    ~Quantity() {
        std::printf("destructor ran value=%d\n", value_);
    }

    int value() const { return value_; }

private:
    int value_;
};

#endif
```

**CR5.** `Quantity(int value)` takes an argument, and `: value_(value)`, which runs
before its body, is a **member initializer list** — it builds the data member `value_`
with the value `value` before the body runs. Writing `value_ = value` inside the body
would give the same result here too, but the initializer list separates construction
from assignment: by the time the body runs, `value_` already exists, it is only being
modified.

## Implicit Construction and explicit

`Quantity(int value)` is a constructor that takes exactly **one** `int` argument; C++
also uses a constructor like this for **implicit type conversion**. If a function like
`show(Quantity q)` is called as `show(5)`, the compiler implicitly calls this
constructor to convert `5` into a `Quantity` — the constructed object has no name, no
variable is visible in `main`, but the object genuinely exists and its constructor
genuinely runs.

```cpp
// temporary-measurement.cpp — is a quantity implicitly built from an int a temporary
#include <cstdio>

#include "quantity.hpp"

static void show(Quantity q) {
    std::printf("inside show value=%d\n", q.value());
}

int main() {
    std::printf("before call\n");
    show(5);
    std::printf("after call\n");
    return 0;
}
```

```text
before call
constructor ran value=5
inside show value=5
destructor ran value=5
after call
```

**CR6.** In the call `show(5)`, `5` is first converted into a `Quantity` object through
`Quantity(5)` — this object has no name, and is therefore **temporary**. This temporary
is copied into `show`'s parameter `q` (or built directly in its place — a compiler
optimization outside this course's scope); when `show` returns, `q`'s destructor is
called, and the output order shows this: the constructor runs right after the call, the
destructor runs right after `show` returns, but **before** the `after call` line — the
temporary's lifetime is bounded by the expression `show(5);` alone, it does not spill
into the rest of `main`.

This implicit conversion is not always wanted: when `show(5)` is written, it is not
obvious to a reader whether `5` is meant to pass as a `Quantity` or is an integer passed
by mistake. The `explicit` keyword prevents this: had `explicit Quantity(int value)`
been written, `show(5)` would not compile, and only an explicit construction such as
`show(Quantity(5))` or `show(Quantity{5})` would compile. This lesson leaves `Quantity`
without `explicit` because what it wants to measure is exactly the temporary that
implicit construction produces; but a class author should consider marking every
single-argument constructor `explicit` — the constructors written in this course's later
lessons are also written as `explicit` unless stated otherwise.

The risk of implicit conversion is not only readability. When a chain of two arguments
allows implicit construction — for example, if `Quantity` implicitly converted from
another class's constructor as well — the compiler could compile a call through an
unexpected chain; the C++ standard limits this by allowing **at most one** implicit
user-defined conversion per call, but even a single-step implicit conversion can hide a
constructor call the reader did not expect. In the `show(5)` example this risk is small,
because `Quantity` has only one constructor and the intent is clear; in a large class
hierarchy, the same implicit construction can silently add the cost of building an
object (a constructor running, a destructor running) that the caller did not notice.
`explicit` removes this silence: every construction is then forced to appear in the
source code as a visible `Type(...)` call.

## Constructor Chains and Reverse-Order Destruction

In a class with more than one data member, the member initializer list ensures that
members are built in **declaration order** — not the order written in the list, the
order of declaration in the class body governs. Destruction happens in exactly the
opposite order: the member built last is destroyed first.

```cpp
// pair.cpp — the construction and destruction order of more than one member
#include <cstdio>

#include "quantity.hpp"

class Pair {
public:
    Pair() : first_(1), second_(2) {
        std::printf("pair constructor ran\n");
    }

private:
    Quantity first_;
    Quantity second_;
};

int main() {
    std::printf("block started\n");
    {
        Pair p;
        std::printf("pair constructed\n");
    }
    std::printf("block ended\n");
    return 0;
}
```

```text
block started
constructor ran value=1
constructor ran value=2
pair constructor ran
pair constructed
destructor ran value=2
destructor ran value=1
block ended
```

**CR7.** Before `Pair`'s own constructor body runs, `first_` and `second_` are built in
the class body's declaration order (not the order in the member initializer list — here
the two coincide, so no difference is visible, but the order is always determined by
declaration); each build triggers its own `Quantity` constructor, which is why two
separate `constructor ran` lines appear before the `pair constructor ran` line. When `p`
is destroyed as the inner block closes, `Pair`'s own destructor (generated by the
compiler because none is written here, printing nothing) runs first, and then its
members are destroyed in **reverse order**: `second_` first, `first_` after. Construction
and destruction mirror each other.

In M10/K02 (Rust), a value going out of scope ran `Drop`; C++'s destructor answers the
same idea, but the triggering rules differ, and this lesson does not deepen that
comparison.

Declaration order being binding means the member initializer list's **written** order is
ignored — some compilers report as a warning when the list's order does not match the
declaration order, because the two orders differing can mislead a reader: even if the
list were written as `second_(2), first_(1)`, construction order would still put
`first_` first, because `first_` is declared first in the class body. `Pair` writes the
list in the same order as the declaration for this lesson, so the two orders coincide
and no confusion arises — but the rule is always declaration order, never the order
written in the list.

Reverse-order destruction has another consequence: a member's constructor can safely
use a resource built by an earlier member — while `second_` is being built, `first_` is
already fully built, because it precedes it in declaration order. The same guarantee
runs in reverse during destruction: while `second_` is being destroyed, `first_` has not
yet been destroyed, so `second_`'s destructor can, if needed, still safely access
`first_`. This symmetry is the foundation the next lesson's subject, RAII (Resource
Acquisition Is Initialization), is built on: a member holding a resource is not
destroyed before the object holding it goes out of scope.

## Constructors Calling Each Other

When a class has more than one constructor, one constructor can call another by
**delegating** to it, to avoid duplicating code between them: writing `Quantity() :
Quantity(0) {}` defines a constructor that, when called with no arguments, delegates its
work to the single-argument constructor. Delegation is written by calling the class's own
name in the member initializer list — here `Quantity(0)` calls a constructor of the
class itself, not of another member. The delegated-to constructor runs completely
(including its own body), and the delegating constructor's body runs after it.
`Quantity` does not use delegation within this lesson's scope — it has only one
constructor — but this technique is the standard way to keep shared construction logic
between multiple constructors in a single place; it must not be confused with building a
member in the initializer list, since one calls the class's own name and the other calls
a member's name.

## The Default Constructor and Default Values

`Quantity` does not write a no-argument constructor, because it wants a quantity to
always be built with a starting value — writing `Quantity t;` does not compile for this
class, because its one constructor requires an `int`. If a class writes no constructor
at all, the compiler generates a no-argument **default constructor**; this generated
constructor builds every data member with its own default value — members of class type
run their own default constructors, while members of primitive types such as `int`
(unless an initializer is given in the member declaration) are left **uninitialized**,
meaning they carry a random memory value. This is a behavior inherited from C and
unchanged in C++: a class like `Pair` that only has members of class type has a safe
default constructor, because every member runs its own constructor; but a member such as
`int amount_;` left without an initializer is left uninitialized by the default
constructor. The classes written in this course give every member of a primitive type an
explicit starting value, either in the declaration (`int amount_ = 0;`) or in the member
initializer list; reading an uninitialized member is also undefined behavior and is not
run.

## The Bounding Measurement

The expression this lesson measures — the implicitly built `Quantity` inside `show(5)` —
is of kind **temporary**: it has no name, and it ceases to exist at the end of the
expression `show(5);`. But a temporary's lifetime does not always stay this short: a
temporary, if bound to a `const` reference, has its lifetime extended to match the
reference's. Had `const Quantity& r = Quantity(7);` been written, the temporary on the
right would normally cease to exist at the end of the expression, but because it is
bound to `r`, its lifetime is extended to the end of `r`'s scope — the compiler does this
automatically, the programmer writes nothing extra. This extension only applies to
`const` references (and to rvalue references, outside this course's scope); writing
`Quantity& r = Quantity(7);` does not compile, because a non-`const` reference cannot
bind to a temporary. That the reference itself cannot be **rebound** to another temporary
afterward — that a reference, once built, always refers to the same object — is what the
references lesson in the next topic measures.

This boundary explains why temporaries must be used carefully in chains of function
calls: in a call such as `show(Quantity(5))`, the temporary lives only for the duration
of the expression in which `show` runs; if a pointer to the temporary, or a non-`const`
reference to it, is carried out of that expression, that pointer or reference becomes
**dangling** once the expression ends — the memory it points at is no longer valid. That
`Quantity`'s `value()` function returns an `int`, not a `const Quantity&` or a
`Quantity*`, exists exactly to avoid this risk: an `int` is copied as a value, and even
if the temporary itself ceases to exist, the copied value remains valid.

These three rules together — that implicit construction produces a temporary, that
declaration order governs construction and destruction, and that a `const` reference can
extend a temporary's lifetime — show that an object "existing" and an object "being
named" are not the same thing. The `Quantity` inside `show(5)` is built without ever
being assigned to a variable, its constructor runs, it is used, its destructor runs — and
throughout this entire process no name ever points at it. The next lesson's RAII uses
exactly this observation: releasing a resource does not require an object to be named,
only that its lifetime reach its end — the compiler always knows that moment, whether the
object has a name or not.

## Summary

- The expression this lesson measures is of kind **temporary**: the `Quantity`
  implicitly built inside `show(5)` has no name and ceases to exist at the end of the
  expression `show(5);`.
- A constructor carries the same name as the class; a destructor is written with a `~`
  prefix; a destructor takes no arguments and cannot be overloaded.
- A single-argument constructor allows implicit type conversion; `explicit` prevents
  this and allows only explicit construction.
- The member initializer list builds members in **declaration order**; destruction
  happens in the **reverse** order.
- A temporary bound to a `const` reference has its lifetime extended to match the
  reference's scope.

## Next Step

This lesson measured when an object comes to exist and ceases to exist; the next lesson
uses that knowledge to safely release a resource (such as memory or a file handle) —
measuring how a destructor releases a member, by separating a guard from the member
inside it.
