---
title: 'Abstract Classes and Interfaces'
source: 'https://academia.sh/en/courses/cpp-fundamentals/abstract-classes-and-interfaces'
course: 'C++ Fundamentals and Object Design'
language: en
updated: '2026-09-26T03:02:46+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Explains how a pure virtual function prevents an object from being constructed directly from the base class'
    - 'Measures that even an inherited function with a body from the base class always works with the derived object'
    - 'Separates the difference between an abstract class and a data-free interface class with an example'
---

# Abstract Classes and Interfaces

The measurement of how a pure virtual function guarantees that no instance can be constructed from the base class, and that an interface is a pure contract carrying no data.

The previous lesson showed that `Shape::introduce` had a body, and could be called
even though derived classes did not redefine it. This lesson covers the case where
a base class is built entirely from functions with no body at all, declaring only a
contract: a **pure virtual function** (marked with `= 0`). No object can be
constructed directly from a class carrying such a function — only from derived
classes that fill it in **completely**. The measurement question is the same: which
object does a call through a base pointer or reference work with — but this time
the base type has no instance of its own at all.

## No Object Can Be Constructed from the Base Class, Only from Derived Ones

`Vehicle`'s `same_as` function is pure virtual (`= 0`); this turns constructing an
object directly from `Vehicle` into a compile error — `Vehicle v;` cannot be
written. Only classes like `Car` and `Bicycle` that fill in `same_as` can have
objects constructed. Does each element in a `Vehicle*` array still reach the
particular object it itself points to?

```cpp
// abstract_base.cpp -- no instance of a class carrying a pure virtual function can be constructed, only of those that fill it in
#include <iostream>

class Vehicle {
public:
    virtual ~Vehicle() = default;
    virtual bool same_as(const void* address) const = 0;
};

class Car : public Vehicle {
public:
    bool same_as(const void* address) const override {
        return static_cast<const void*>(this) == address;
    }
};

class Bicycle : public Vehicle {
public:
    bool same_as(const void* address) const override {
        return static_cast<const void*>(this) == address;
    }
};

int main() {
    Car c;
    Bicycle b;
    Vehicle* vehicles[2] = { &c, &b };

    std::cout << std::boolalpha;
    std::cout << "vehicles[0] same as its own object c "
              << vehicles[0]->same_as(&c) << "\n";
    std::cout << "vehicles[1] same as its own object b "
              << vehicles[1]->same_as(&b) << "\n";
    std::cout << "vehicles[0] same as b (cross-check) "
              << vehicles[0]->same_as(&b) << "\n";

    return 0;
}
```

```
vehicles[0] same as its own object c true
vehicles[1] same as its own object b true
vehicles[0] same as b (cross-check) false
```

**PS7.** If `Vehicle v;` were written, the compiler would reject it — the pure
virtual `same_as` leaves `Vehicle` incomplete on its own, only classes like `Car`
and `Bicycle` that completely fill in the function can be constructed. This means
the only thing a `Vehicle*` can point to is the instance of a concrete class that
fills in `same_as` — there is no possibility at all of falling back to an
instance of the base class itself, a "spare" instance. `vehicles[0]->same_as(&c)`
is **true**, `vehicles[1]->same_as(&b)` is **true**, the cross-check is **false**:
each element reaches its own concrete object. (M08/K02 asked which type resolved
the call; here, since the base type has no instance of its own at all, resolution
always falls to a concrete type — and just as an interface reference in Java can
never point to an object of the pure interface type itself, a pure virtual function
in C++ provides the same restriction.) The class is **self**: the call necessarily
works with a concrete derived object, because the base type itself can never exist.

## An Inherited Body Also Works with the Same Object

Alongside the pure virtual `same_as`, `Vehicle` also carries a `maintenance_info`
with a body. `Car` does not redefine it, `Bicycle` does. When the inherited body
runs, does `this` still carry the address of the called object?

```cpp
// abstract_inherited_function.cpp -- an inherited function with a body also carries the address of the called object
#include <iostream>

class Vehicle {
public:
    virtual ~Vehicle() = default;
    virtual bool same_as(const void* address) const = 0;
    virtual void maintenance_info(const void* expected) const {
        std::cout << "Vehicle::maintenance_info running object same as expected "
                  << std::boolalpha << (static_cast<const void*>(this) == expected) << "\n";
    }
};

class Car : public Vehicle {
public:
    bool same_as(const void* address) const override {
        return static_cast<const void*>(this) == address;
    }
};

class Bicycle : public Vehicle {
public:
    bool same_as(const void* address) const override {
        return static_cast<const void*>(this) == address;
    }
    void maintenance_info(const void* expected) const override {
        std::cout << "Bicycle::maintenance_info running object same as expected "
                  << std::boolalpha << (static_cast<const void*>(this) == expected) << "\n";
    }
};

int main() {
    Car c;
    Bicycle b;
    Vehicle* v1 = &c;
    Vehicle* v2 = &b;

    v1->maintenance_info(&c);
    v2->maintenance_info(&b);

    return 0;
}
```

```
Vehicle::maintenance_info running object same as expected true
Bicycle::maintenance_info running object same as expected true
```

**PS8.** Since `Car` does not redefine `maintenance_info`, the call
`v1->maintenance_info(&c)` runs the body of `Vehicle::maintenance_info` — but even
inside this body, `this` compared against `c`'s address returns **true**. Since
`Bicycle` supplies its own body, `v2->maintenance_info(&b)` runs
`Bicycle::maintenance_info`, and `this` is again `b`'s address. An inherited body
running does not mean `Vehicle` has an instance of its own — that body is only ever
run on a concrete `Car` object. The class is **self**: even the common body
supplied by the base class works with no object other than the concrete object it
was called on.

## An Interface: Only a Contract, No Shared Data

`Vehicle` carried no data, but it did carry a common body — it was a full abstract
class. `Printable` is different: it has no data field, no function with a body at
all, only a contract. `Report` fills in this contract and carries its own data.

```cpp
// interface_contract.cpp -- an interface carries no data at all, the one filling in the contract carries its own data
#include <iostream>
#include <string>
#include <utility>

class Printable {
public:
    virtual ~Printable() = default;
    virtual bool same_as(const void* address) const = 0;
};

class Report : public Printable {
public:
    explicit Report(std::string title) : title_(std::move(title)) {}
    bool same_as(const void* address) const override {
        return static_cast<const void*>(this) == address;
    }
    const std::string& title() const { return title_; }
private:
    std::string title_;
};

void list_item(const Printable& p, const void* expected) {
    std::cout << std::boolalpha
              << "object called inside list_item same as expected "
              << p.same_as(expected) << "\n";
}

int main() {
    Report r("Annual Report");
    std::cout << "the report's title " << r.title() << "\n";
    list_item(r, &r);
    return 0;
}
```

```
the report's title Annual Report
object called inside list_item same as expected true
```

**PS9.** `Printable` declares no data field at all; `title_` belongs entirely to
`Report` — `Printable` never touches this data, it only imposes the `same_as`
contract. When `r` is bound to a `Printable&` and passed into `list_item`,
`p.same_as(&r)` is again **true**: reference binding does not construct a copy here
either, `p` is the same object as `r` inside `main`. The difference between
`Vehicle` and `Printable` is concrete: `Vehicle` can carry data and offer functions
with a common body (abstract class), `Printable` is purely signatures, no data, no
body at all (interface). The class is **self**: in both cases, the call always
works with the concrete object that fills in the contract.

## A Pure Virtual Function Can Have a Body, But Only Through a Qualified Call

`= 0` makes a function pure virtual and prevents the carrying class from being
instantiated — but this does not mean the function can never have a body.
`NoisyVehicle::warn` is both pure virtual and has a body. Is this body reached
through an ordinary virtual call, or some other way?

```cpp
// abstract_pure_virtual_body.cpp -- a pure virtual function can have a body, but it only runs through a qualified call
#include <iostream>

class NoisyVehicle {
public:
    virtual ~NoisyVehicle() = default;
    virtual void warn() const = 0;
};

void NoisyVehicle::warn() const {
    std::cout << "NoisyVehicle::warn (default body) ran\n";
}

class Truck : public NoisyVehicle {
public:
    void warn() const override {
        std::cout << "Truck::warn ran, now calling the base body through a qualified call\n";
        NoisyVehicle::warn();
    }
};

int main() {
    Truck t;
    NoisyVehicle& base_ref = t;
    base_ref.warn();
    return 0;
}
```

```
Truck::warn ran, now calling the base body through a qualified call
NoisyVehicle::warn (default body) ran
```

**PS28.** `base_ref.warn()` is an ordinary virtual call — and since `Truck`
redefines `warn`, it never reaches `NoisyVehicle`'s body on its own, just like every
`override` in the previous lessons. `NoisyVehicle::warn`'s body only runs when
manually called with a **qualified** call (`NoisyVehicle::warn();`) from inside
`Truck::warn` — this line bypasses virtual dispatch and targets that name directly.
"Pure virtual" and "bodiless" diverge here: `= 0` is what prevents the class from
being instantiated and forces derived classes to fill in the function; whether it
has a body is a separate question, and if it does, that body is reachable only
through a qualified call, not ordinary virtual dispatch. The class is **self**:
even the qualified call works on `t` itself, only name resolution determines which
body runs.

## No Instance of an Abstract Base Can Be Constructed, But a Concrete Base Can Be Taken by Value and Sliced

Because `Vehicle` carries a pure virtual function, `Vehicle v;` never compiles at
all — this means a **value** of type `Vehicle` can also never exist, so the risk of
object slicing cannot even arise for `Vehicle`. How does the situation change when
a base class is not abstract, and has a full body?

```cpp
// abstract_concrete_base_by_value.cpp -- no instance of an abstract base can be constructed, but a concrete base can be taken by value and sliced
#include <iostream>
#include <string>

class VehicleRecord {
public:
    explicit VehicleRecord(std::string plate) : plate_(std::move(plate)) {}
    std::string plate() const { return plate_; }
protected:
    std::string plate_;
};

class DetailedRecord : public VehicleRecord {
public:
    DetailedRecord(std::string plate, int km)
        : VehicleRecord(std::move(plate)), km_(km) {}
    int km() const { return km_; }
private:
    int km_;
};

int main() {
    DetailedRecord detailed("34ABC123", 15000);
    VehicleRecord sliced = detailed;

    std::cout << std::boolalpha;
    std::cout << "sliced and detailed carry the same address "
              << (static_cast<void*>(&sliced) == static_cast<const void*>(&detailed)) << "\n";
    std::cout << "sliced object's plate " << sliced.plate() << "\n";

    return 0;
}
```

```
sliced and detailed carry the same address false
sliced object's plate 34ABC123
```

**PS29.** `VehicleRecord` carries no pure virtual function, it is a concrete class
— so `VehicleRecord sliced = detailed;` compiles and produces the same slicing
measured in PS21 in the Inheritance lesson: `sliced` is a separate object living at
its own address, independent of `detailed` (**false**), and the `km_` field
`DetailedRecord` added is not carried over at all. `Vehicle` has no counterpart to
this line, because defining a variable of type `Vehicle` is itself a compile error
— no `Vehicle` value that could be the target of slicing can ever exist.
Abstractness means not only "the obligation to redefine," but also "a value of the
base type can never come into existence": an abstract base can be accessed by
pointer or reference, but can never be placed by value into a variable, parameter,
or return value — this also makes it structurally immune to slicing. The slicing
risk exists only for concrete classes. The class is **copy**: `sliced` is an
independent copy of `detailed`'s base part.

## The Role of an Interface: Handling Unrelated Types Under One Contract

`Report` and `Invoice` do not derive from each other, they share no data or
behavior at all — their only common point is filling in the `Printable` contract.
Can these two be processed uniformly, in the same vector, without their concrete
types being known?

```cpp
// interface_role_multiple_types.cpp -- different concrete types can be processed uniformly through the same interface
#include <iostream>
#include <string>
#include <vector>

class Printable {
public:
    virtual ~Printable() = default;
    virtual std::string text() const = 0;
};

class Report : public Printable {
public:
    explicit Report(std::string title) : title_(std::move(title)) {}
    std::string text() const override { return "Report: " + title_; }
private:
    std::string title_;
};

class Invoice : public Printable {
public:
    explicit Invoice(int amount) : amount_(amount) {}
    std::string text() const override { return "Invoice: " + std::to_string(amount_); }
private:
    int amount_;
};

int main() {
    Report r("Annual Report");
    Invoice i(150);
    std::vector<Printable*> documents{ &r, &i };

    for (const auto* doc : documents) {
        std::cout << doc->text() << "\n";
    }

    return 0;
}
```

```
Report: Annual Report
Invoice: 150
```

**PS30.** `Report` and `Invoice` are not related in the same hierarchy tree, they
share no data field or function with a body — their only bond is that both fill in
`Printable`. The `documents` vector holds both under a single `Printable*` type,
and the loop calls `text()` without ever knowing the element's concrete type; each
call still reaches the correct concrete type's function: "Report: Annual Report"
and "Invoice: 150". This shows the real role of an interface: unlike an abstract
class, an interface does not establish kinship, it only imposes a contract by
signature — every type that fills in the contract can be processed in the same
collection, the same loop, even with no other commonality between them. The class
is **self**: every `text()` call works with the very concrete object the element in
the loop points to.

## An Interface Also Needs a Virtual Destructor

Although `Printable` appears to offer only a contract, being able to `delete` a
concrete derived object like `Report` through a `Printable*` still requires its own
destructor to be virtual. Does this lesson confirm that?

```cpp
// interface_virtual_destructor.cpp -- an interface's virtual destructor runs the full destruction chain of the derived object
#include <iostream>

class Printable {
public:
    virtual ~Printable() { std::cout << "Printable destructor\n"; }
    virtual void print() const = 0;
};

class Report : public Printable {
public:
    ~Report() override { std::cout << "Report destructor\n"; }
    void print() const override { std::cout << "Report::print\n"; }
};

int main() {
    Printable* doc = new Report();
    doc->print();
    std::cout << "-- deletion begins --\n";
    delete doc;
    return 0;
}
```

```
Report::print
-- deletion begins --
Report destructor
Printable destructor
```

**PS31.** `doc`'s static type is `Printable*`, but `delete doc` runs **Report
destructor** first, then **Printable destructor** — the same principle measured as
PS24 in the Virtual Functions and Vtables lesson, here in the interface context.
Even though `Printable` is a contract offering only pure virtual functions, if its
destructor were not `virtual`, that same `delete doc` would call only
`Printable`'s own destructor, `Report`'s resources never being released — and with
the static and dynamic types differing, deleting through a non-virtual destructor
would be undefined behavior. This branch, as in PS24, is not run here because it
would not give a deterministic output. Conclusion: "offering only a contract" does
not exempt a class from the virtual-destructor requirement — if polymorphic
deletion through a base pointer is going to happen, the destructor must be virtual,
interface included. The class is **self**: the destruction chain always operates on
the real `Report` object `doc` points to.

## The Bounding Measurement

The abstractness measured in this lesson is under **single inheritance**: each
concrete class filled in exactly one abstract base class or interface. The case
where a class derives from multiple base classes at once, filling in multiple
contracts simultaneously — multiple inheritance — is outside this lesson's scope;
the next lesson measures that. That a pure virtual function can also have a body
was shown with only one example (`NoisyVehicle::warn`); which designs really need
that body was not generalized. That a line like `Vehicle v;` really produces a
compile error was not shown by running it in this lesson, because validation only
runs code that compiles successfully — this is a fact stated in the lesson text but
outside the code that was run.

## Summary

- No object can be constructed directly from a class carrying a pure virtual
  function (`= 0`); only from derived classes that fill in that function.
- Each element in a `Vehicle*` array reaches its own concrete object, because the
  base type has no instance of its own at all — the class is **self**.
- Even an inherited function with a body supplied by the base class works with no
  object other than the concrete object it was called on.
- An abstract class (`Vehicle`) can carry data and offer functions with a common
  body; an interface (`Printable`) is purely signatures, carrying no data, no body
  at all.
- An object passed into an interface reference is the same object inside the
  function as it is outside — reference binding does not construct a copy here
  either.

## Next Step

In this lesson, every concrete class filled in exactly one abstract base class or
interface; the base subobject always stood at the head of the derived object, just
like in the Inheritance lesson. The next lesson, Multiple Inheritance and the
Diamond Problem, measures the case where a class derives from **more than one**
base class at once — there, the second and subsequent base subobjects may no
longer stand at the head of the derived object, their addresses may not coincide
with the derived object's address. What is measured is again which object an
expression works with, but this time **which part** of the derived object the base
subobject stands in is also part of the question.
