Chapter 10 — Key Takeaways (Object-Oriented Fortran)
A one-page reference for inheritance, polymorphism, select type, abstract/deferred, and final — and the
one rule that keeps them fast.
The syntax, at a glance
| Construct | Syntax | What it does |
|---|---|---|
| Type extension | type, extends(parent_t) :: child_t |
Inherit all of parent_t; add and/or override. |
| Parent part | self%parent_t |
Access/call the inherited (base) part explicitly. |
| Call parent's method | call self%parent_t%method() |
Non-polymorphic call of the overridden base version. |
| Polymorphic variable | class(base_t), allocatable :: x |
Holds base_t or any extension (dynamic type may vary). |
| Abstract type | type, abstract :: t |
Cannot be instantiated; only extended. |
| Deferred binding | procedure(iface), deferred :: m |
Contract: every concrete extension must implement m. |
| Abstract interface | abstract interface … end interface |
Specifies a deferred binding's signature (needs import). |
| Run-time type test | select type (o => x) → type is / class is / class default |
Run a block per dynamic type. |
| Destructor | final :: sub (arg is type(t), intent(inout)) |
Runs just before an object is destroyed. |
| Typed allocation | allocate(child_t :: x) |
Allocate polymorphic x with dynamic type child_t. |
| Sourced allocation | allocate(x, source = child_t(...)) |
Allocate and set dynamic type and value. |
type vs class — the core distinction
type(t) |
class(t) |
|
|---|---|---|
| Kind | Monomorphic | Polymorphic |
| Dynamic type | Always t |
t or any extension |
| Resolved | Compile time | Run time (dispatch) |
| Inlinable | Yes | No (indirect call) |
| Local variable | Any | Must be allocatable/pointer/dummy |
| Use it for | The default; all hot-loop data | Genuine run-time variation only |
select type guards
type is (circle_t)— matches the exact dynamic typecircle_t.class is (shape_t)— matchesshape_tor any extension; most specific match wins.class default— everything unmatched.- Smell: a chain of
type isthat implements behavior should usually be adeferredbinding instead.
Which construct, when
| You want to… | Use |
|---|---|
| Share data/behavior across related types | extends (inheritance) |
| One interface, many interchangeable implementations | abstract type + deferred binding |
| A variable that holds different types at run time | class(...), allocatable/pointer |
| A mixed collection of types | array of a box type wrapping class(...), allocatable :: obj |
| Type-specific code at a boundary (rare) | select type |
| Clean up an unmanaged resource on destruction | final procedure |
Common pitfalls
class(t) :: xas a plain local → error; make itallocatable/pointer/dummy.type(t)wheretis abstract → error; abstract types cannot be instantiated.finalwithclass(t)→ wrong; a finalizer takestype(t), intent(inout).- Positional constructor for an extended type → inherited components come first;
circle_t(2.0_dp)may hitlabel. Use keywords:circle_t(radius = 2.0_dp). - Forgetting
importin anabstract interface→ the type/kind is "not defined"; the interface body is its own scope. Addimport :: t(anduseyour kind, orimportit). - A bare
class(t), allocatable :: arr(:)cannot hold a mix of dynamic types → use an array of boxes.
The performance rule (memorize this)
Put polymorphism at the coarse grain; never in the hot inner loop. Dispatch once per timestep (which solver) — free. Dispatch once per cell — ruinous: an indirect call cannot be inlined, which blocks fusion and vectorization. Abstract the solver; keep the cells flat, monomorphic, and contiguous.
Numbers & facts worth keeping
- Object orientation entered Fortran in the 2003 standard.
- A dispatched call: order of tens of cycles, and — more costly — it blocks inlining/vectorization (Tier-2 order of magnitude; the direction is certain).
- Allocatable components self-deallocate when their object is destroyed → you rarely need
final.
Project piece added this chapter (optional/advanced track)
An abstract solver_t with a deferred step(self, fld) binding, and a heat_solver_t that
extends it — so the heat solver is one interchangeable implementation behind one interface. The serial path
keeps the plain heat_solver module from Chapter 8. Dispatch is once per timestep; the stencil kernel stays
monomorphic. Feeds the Chapter 38
capstone as the seam where parallel and implicit solvers slot in. (See code/project-checkpoint.f90.)
Key terms
class · type extension · polymorphism · select type · abstract type ·
deferred binding · final · (used from earlier: derived type, component, type-bound procedure,
module — Ch. 8–9).