Inheritance - D Programming Language

Menu

- Learn
- Documentation
  - Language Reference
  - Library Reference
  - Command-line Reference
  - Feature Overview
  - Articles
- Downloads
- Packages
- Community
  - Blog
  - Orgs using D
  - Twitter
  - Calendar
  - Discord (community-run)
  - Forums
  - IRC
  - Wiki
  - Issues
  - Contributing
  - Contributors
  - Foundation
  - Security Team
  - Donate
  - Sponsors
- Resources
  - Tour
  - Books
  - Tutorials
  - Tools
  - Editors
  - IDEs
  - run.dlang.io
  - Visual D
  - Acknowledgments
  - D Style
  - Glossary
  - Sitemap

Search
Report a bug
If you spot a problem with this page, click here to create a GitHub issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

# Inheritance

Inheritance is defining a more specialized type based on an existing more general base type. The specialized type acquires the members of the base type and as a result can be substituted in place of the base type.

Inheritance is available for classes, not structs. The class that inherits another class is called the subclass, and the class that gets inherited is called the superclass, also called the base class.

There are two types of inheritance in D. We will cover implementation inheritance in this chapter and leave interface inheritance to a later chapter.

When defining a subclass, the superclass is specified after a colon character:

    class SubClass : SuperClass {
        // ...
    }

To see an example of this, let's assume that there is already the following class that represents a clock:

    class Clock {
        int hour;
        int minute;
        int second;

        void adjust(int hour, int minute, int second = 0) {
            this.hour = hour;
            this.minute = minute;
            this.second = second;
        }
    }

Apparently, the members of that class do not need special values during construction; so there is no constructor. Instead, the members are set by the `adjust()` member function:

        auto deskClock = new Clock;
        deskClock.adjust(20, 30);
        writefln(
            "%02s:%02s:%02s",
            deskClock.hour, deskClock.minute, deskClock.second);

Note: It would be more useful to produce the time string by a `toString()` function. It will be added later when explaining the `override` keyword below.

The output:

    20:30:00

With only that much functionality, `Clock` could be a struct as well, and depending on the needs of the program, that could be sufficient.

However, being a class makes it possible to inherit from `Clock`.

To see an example of inheritance, let's consider an `AlarmClock` that not only includes all of the functionality of `Clock`, but also provides a way of setting the alarm. Let's first define this type without regard to `Clock`. If we did that, we would have to include the same three members of `Clock` and the same `adjust()` function that adjusted them. `AlarmClock` would also have other members for its additional functionality:

    class AlarmClock {
        int hour;
        int minute;
        int second;
        int alarmHour;
        int alarmMinute;

        void adjust(int hour, int minute, int second = 0) {
            this.hour = hour;
            this.minute = minute;
            this.second = second;
        }

        void adjustAlarm(int hour, int minute) {
            alarmHour = hour;
            alarmMinute = minute;
        }
    }

The members that appear exactly in `Clock` are highlighted. As can be seen, defining `Clock` and `AlarmClock` separately results in code duplication.

Inheritance is helpful in such cases. Inheriting `AlarmClock` from `Clock` simplifies the new class and reduces code duplication:

    class AlarmClock : Clock {
        int alarmHour;
        int alarmMinute;

        void adjustAlarm(int hour, int minute) {
            alarmHour = hour;
            alarmMinute = minute;
        }
    }

The new definition of `AlarmClock` is the equivalent of the previous one. The highlighted part of the new definition corresponds to the highlighted parts of the old definition.

Because `AlarmClock` inherits the members of `Clock`, it can be used just like a `Clock`:

        auto bedSideClock = new AlarmClock;
        bedSideClock.adjust(20, 30);
        bedSideClock.adjustAlarm(7, 0);

The members that are inherited from the superclass can be accessed as if they were the members of the subclass:

        writefln("%02s:%02s:%02s ♫%02s:%02s",
                 bedSideClock.hour,
                 bedSideClock.minute,
                 bedSideClock.second,
                 bedSideClock.alarmHour,
                 bedSideClock.alarmMinute);

The output:

    20:30:00 ♫07:00

Note: An `AlarmClock.toString` function would be more useful in this case. It will be defined later below.

The inheritance used in this example is implementation inheritance.

If we imagine the memory as a ribbon going from top to bottom, the placement of the members of `AlarmClock` in memory can be pictured as in the following illustration:

                                │      .      │
                                │      .      │
    the address of the object → ├─────────────┤
                                │(other data) │
                                │ hour        │
                                │ minute      │
                                │ second      │
                                │ alarmHour   │
                                │ alarmMinute │
                                ├─────────────┤
                                │      .      │
                                │      .      │

The illustration above is just to give an idea on how the members of the superclass and the subclass may be combined together. The actual layout of the members depends on the implementation details of the compiler in use. For example, the part that is marked as other data typically includes the pointer to the virtual function table (vtbl) of that particular class type. The details of the object layout are outside the scope of this book.

##### Warning: Inherit only if "is a"

We have seen that implementation inheritance is about acquiring members. Consider this kind of inheritance only if the subtype can be thought of being a kind of the supertype as in the phrase "alarm clock is a clock."

"Is a" is not the only relationship between types; a more common relationship is the "has a" relationship. For example, let's assume that we want to add the concept of a `Battery` to the `Clock` class. It would not be appropriate to add `Battery` to `Clock` by inheritance because the statement "clock is a battery" is not true:

    class Clock : Battery {    // ← WRONG DESIGN
        // ...
    }

A clock is not a battery; it has a battery. When there is such a relationship of containment, the type that is contained must be defined as a member of the type that contains it:

    class Clock {
        Battery battery;       // ← Correct design
        // ...
    }

##### Inheritance from at most one class

Classes can only inherit from a single base class (which itself can potentially inherit from another single class). In other words, multiple inheritance is not supported in D.

For example, assuming that there is also a `SoundEmitter` class, and even though "alarm clock is a sound emitting object" is also true, it is not possible to inherit `AlarmClock` both from `Clock` and `SoundEmitter`:

    class SoundEmitter {
        // ...
    }

    class AlarmClock : Clock, SoundEmitter {    // ← derleme HATASI
        // ...
    }

On the other hand, there is no limit to the number of interfaces that a class can inherit from. We will see the `interface` keyword in a later chapter.

Additionally, there is no limit to how deep the inheritance hierarchy can go:

    class MusicalInstrument {
        // ...
    }

    class StringInstrument : MusicalInstrument {
        // ...
    }

    class Violin : StringInstrument {
        // ...
    }

The inheritance hierarchy above defines a relationship from the more general to the more specific: musical instrument, string instrument, and violin.

##### Hierarchy charts

Types that are related by the "is a" relationship form a class hierarchy.

According to OOP conventions, class hierarchies are represented by superclasses being on the top and the subclasses being at the bottom. The inheritance relationships are indicated by arrows pointing from the subclasses to the superclasses.

For example, the following can be a hierarchy of musical instruments:

                 MusicalInstrument
                    ↗         ↖
        StringInstrument   WindInstrument
             ↗    ↖            ↗    ↖
         Violin  Guitar    Flute   Recorder

##### Accessing superclass members

The `super` keyword allows referring to members that are inherited from the superclass.

    class AlarmClock : Clock {
        // ...

        void foo() {
            super.minute = 10; // The inherited 'minute' member
            minute = 10;       // Same thing if there is no ambiguity
        }
    }

The `super` keyword is not always necessary; `minute` alone has the same meaning in the code above. The `super` keyword is needed when both the superclass and the subclass have members under the same names. We will see this below when we will need to write `super.reset()` and `super.toString()`.

If multiple classes in an inheritance tree define a symbol with the same name, one can use the specific name of the class in the inheritance tree to disambiguate between the symbols:

    class Device {
        string manufacturer;
    }

    class Clock : Device {
        string manufacturer;
    }

    class AlarmClock : Clock {
        // ...

        void foo() {
            Device.manufacturer = "Sunny Horology, Inc.";
            Clock.manufacturer = "Better Watches, Ltd.";
        }
    }

##### Constructing superclass members

The other use of the `super` keyword is to call the constructor of the superclass. This is similar to calling the overloaded constructors of the current class: `this` when calling constructors of the current class and `super` when calling constructors of the superclass.

It is not required to call the superclass constructor explicitly. If the constructor of the subclass makes an explicit call to any overload of `super`, then that constructor is executed by that call. Otherwise, and if the superclass has a default constructor, it is executed automatically before entering the body of the subclass.

We have not defined constructors for the `Clock` and `AlarmClock` classes yet. For that reason, the members of both of those classes are initialized by the `.init` values of their respective types, which is 0 for `int`.

Let's assume that `Clock` has the following constructor:

    class Clock {
        this(int hour, int minute, int second) {
            this.hour = hour;
            this.minute = minute;
            this.second = second;
        }

        // ...
    }

That constructor must be used when constructing `Clock` objects:

        auto clock = new Clock(17, 15, 0);

Naturally, the programmers who use the `Clock` type directly would have to use that syntax. However, when constructing an `AlarmClock` object, they cannot construct its `Clock` part separately. Besides, the users of `AlarmClock` need not even know that it inherits from `Clock`.

A user of `AlarmClock` should simply construct an `AlarmClock` object and use it in the program without needing to pay attention to its `Clock` heritage:

        auto bedSideClock = new AlarmClock(/* ... */);
        // ... use as an AlarmClock ...

For that reason, constructing the superclass part is the responsibility of the subclass. The subclass calls the constructor of the superclass with the `super()` syntax:

    class AlarmClock : Clock {
        this(int hour, int minute, int second,  // for Clock's members
             int alarmHour, int alarmMinute) {  // for AlarmClock's members
            super(hour, minute, second);
            this.alarmHour = alarmHour;
            this.alarmMinute = alarmMinute;
        }

        // ...
    }

The constructor of `AlarmClock` takes arguments for both its own members and the members of its superclass. It then uses part of those arguments to construct its superclass part.

##### Overriding the definitions of member functions

One of the benefits of inheritance is being able to redefine the member functions of the superclass in the subclass. This is called overriding: The existing definition of the member function of the superclass is overridden by the subclass with the `override` keyword.

Overridable functions are called virtual functions. Virtual functions are implemented by the compiler through virtual function pointer tables (vtbl) and vtbl pointers. The details of this mechanism are outside the scope of this book. However, it must be known by every system programmer that virtual function calls are more expensive than regular function calls. Every non-private `class` member function in D is virtual by default. For that reason, when a superclass function does not need to be overridden at all, it should be defined as `final` so that it is not virtual. We will see the `final` keyword later in the Interfaces chapter.

Let's assume that `Clock` has a member function that is used for resetting its members all to zero:

    class Clock {
        void reset() {
            hour = 0;
            minute = 0;
            second = 0;
        }

        // ...
    }

That function is inherited by `AlarmClock` and can be called on an `AlarmClock` object:

        auto bedSideClock = new AlarmClock(20, 30, 0, 7, 0);
        // ...
        bedSideClock.reset();

However, necessarily ignorant of the members of `AlarmClock`, `Clock.reset` can only reset its own members. For that reason, to reset the members of the subclass as well, `reset()` must be overridden:

    class AlarmClock : Clock {
        override void reset() {
            super.reset();
            alarmHour = 0;
            alarmMinute = 0;
        }

        // ...
    }

The subclass resets only its own members and dispatches the rest of the task to `Clock` by the `super.reset()` call. Note that writing just `reset()` would not work as it would call the `reset()` function of `AlarmClock` itself. Calling `reset()` from within itself would cause an infinite recursion.

The reason that I have delayed the definition of `toString()` until this point is that it must be defined by the `override` keyword for classes. As we will see in the next chapter, every class is automatically inherited from a superclass called `Object` and `Object` already defines a `toString()` member function.

For that reason, the `toString()` member function for classes must be defined by using the `override` keyword:

    import std.string;

    class Clock {
        override string toString() const {
            return format("%02s:%02s:%02s", hour, minute, second);
        }

        // ...
    }

    class AlarmClock : Clock {
        override string toString() const {
            return format("%s ♫%02s:%02s", super.toString(),
                          alarmHour, alarmMinute);
        }

        // ...
    }

Note that `AlarmClock` is again dispatching some of the task to `Clock` by the `super.toString()` call.

Those two overrides of `toString()` allow converting `AlarmClock` objects to strings:

    void main() {
        auto deskClock = new AlarmClock(10, 15, 0, 6, 45);
        writeln(deskClock);
    }

The output:

    10:15:00 ♫06:45

##### Using the subclass in place of the superclass

Since the superclass is more general and the subclass is more specialized, objects of a subclass can be used in places where an object of the superclass type is required. This is called polymorphism.

The concepts of general and specialized types can be seen in statements like "this type is of that type": "alarm clock is a clock", "student is a person", "cat is an animal", etc. Accordingly, an alarm clock can be used where a clock is needed, a student can be used where a person is needed, and a cat can be used where an animal is needed.

When a subclass object is being used as a superclass object, it does not lose its own specialized type. This is similar to the examples in real life: Using an alarm clock simply as a clock does not change the fact that it is an alarm clock; it would still behave like an alarm clock.

Let's assume that a function takes a `Clock` object as parameter, which it resets at some point during its execution:

    void use(Clock clock) {
        // ...
        clock.reset();
        // ...
    }

Polymorphism makes it possible to send an `AlarmClock` to such a function:

        auto deskClock = new AlarmClock(10, 15, 0, 6, 45);
        writeln("Before: ", deskClock);
        use(deskClock);
        writeln("After : ", deskClock);

This is in accordance with the relationship "alarm clock is a clock." As a result, the members of the `deskClock` object get reset:

    Before: 10:15:00 ♫06:45
    After : 00:00:00 ♫00:00

The important observation here is that not only the members of `Clock` but also the members of `AlarmClock` have been reset.

Although `use()` calls `reset()` on a `Clock` object, since the actual object is an `AlarmClock`, the function that gets called is `AlarmClock.reset`. According to its definition above, `AlarmClock.reset` resets the members of both `Clock` and `AlarmClock`.

In other words, although `use()` uses the object as a `Clock`, the actual object may be an inherited type that behaves in its own special way.

Let's add another class to the `Clock` hierarchy. The `reset()` function of this type sets its members to random values:

    import std.random;

    class BrokenClock : Clock {
        this() {
            super(0, 0, 0);
        }

        override void reset() {
            hour = uniform(0, 24);
            minute = uniform(0, 60);
            second = uniform(0, 60);
        }
    }

When an object of `BrokenClock` is sent to `use()`, then the special `reset()` function of `BrokenClock` would be called. Again, although it is passed as a `Clock`, the actual object is still a `BrokenClock`:

        auto shelfClock = new BrokenClock;
        use(shelfClock);
        writeln(shelfClock);

The output shows random time values as a result of resetting a `BrokenClock`:

    22:46:37

##### Inheritance is transitive

Polymorphism is not just limited to two classes. Subclasses of subclasses can also be used in place of any superclass in the hierarchy.

Let's consider the `MusicalInstrument` hierarchy:

    class MusicalInstrument {
        // ...
    }

    class StringInstrument : MusicalInstrument {
        // ...
    }

    class Violin : StringInstrument {
        // ...
    }

The inheritances above builds the following relationships: "string instrument is a musical instrument" and "violin is a string instrument." Therefore, it is also true that "violin is a musical instrument." Consequently, a `Violin` object can be used in place of a `MusicalInstrument`.

Assuming that all of the supporting code below has also been defined:

    void playInTune(MusicalInstrument instrument,
                    MusicalPiece piece) {
        instrument.tune();
        instrument.play(piece);
    }

    // ...

    auto myViolin = new Violin;
    playInTune(myViolin, improvisation);

Although `playInTune()` expects a `MusicalInstrument`, it is being called with a `Violin` due to the relationship "violin is a musical instrument."

Inheritance can be as deep as needed.

##### Abstract member functions and abstract classes

Sometimes there are member functions that are natural to appear in a class interface even though that class cannot provide its definition. When there is no concrete definition of a member function, that function is an abstract member function. A class that has at least one abstract member function is an abstract class.

For example, the `ChessPiece` superclass in a hierarchy may have an `isValid()` member function that determines whether a given move is valid for that chess piece. Since validity of a move depends on the actual type of the chess piece, the `ChessPiece` general class cannot make this decision itself. The valid moves can only be known by the subclasses like `Pawn`, `King`, etc.

The `abstract` keyword specifies that the inherited class must implement such a method itself:

    class ChessPiece {
        abstract bool isValid(Square from, Square to);
    }

It is not possible to construct objects of abstract classes:

        auto piece = new ChessPiece;    // ← derleme HATASI

The subclass would have to override and implement all the abstract functions in order to make the class non-abstract and therefore constructible:

    class Pawn : ChessPiece {
        override bool isValid(Square from, Square to) {
            // ... the implementation of isValid for pawn ...
            return decision;
        }
    }

It is now possible to construct objects of `Pawn`:

        auto piece = new Pawn;             // compiles

Note that an abstract function may have an implementation of its own, but it would still require the subclass to provide its own implementation of such a function. For example, the `ChessPiece`'es implementation may provide some useful checks of its own:

    class ChessPiece {
        abstract bool isValid(Square from, Square to) {
            // We require the 'to' position to be different than
            // the 'from' position
            return from != to;
        }
    }

    class Pawn : ChessPiece {
        override bool isValid(Square from, Square to) {
            // First verify if it is a valid move for any ChessPiece
            if (!super.isValid(from, to)) {
                return false;
            }

            // ... then check if it is valid for the Pawn ...

            return decision;
        }
    }

The `ChessPiece` class is still abstract even though `isValid()` was already implemented, but the `Pawn` class is non-abstract and can be instantiated.

##### Example

Let's consider a class hierarchy that represents railway vehicles:

               RailwayVehicle
              /      |       \
      Locomotive   Train   RailwayCar { load()?, unload()? }
                              /   \
                   PassengerCar   FreightCar

The functions that `RailwayCar` will declare as `abstract` are indicated by question marks.

Since my goal is only to present a class hierarchy and point out some of its design decisions, I will not fully implement these classes. Instead of doing actual work, they will simply print messages.

The most general class of the hierarchy above is `RailwayVehicle`. In this program, it will only know how to move itself:

    class RailwayVehicle {
        void advance(size_t kilometers) {
            writefln("The vehicle is advancing %s kilometers",
                     kilometers);
        }
    }

A class that inherits from `RailwayVehicle` is `Locomotive`, which does not have any special members yet:

    class Locomotive : RailwayVehicle {
    }

We will add a special `makeSound()` member function to `Locomotive` later during one of the exercises.

`RailwayCar` is a `RailwayVehicle` as well. However, if the hierarchy supports different types of railway cars, then certain behaviors like loading and unloading must be done according to their exact types. For that reason, `RailwayCar` can only declare these two functions as abstract:

    class RailwayCar : RailwayVehicle {
        abstract void load();
        abstract void unload();
    }

Loading and unloading a passenger car is as simple as opening the doors of the car, while loading and unloading a freight car may involve porters and winches. The following subclasses provide definitions for the abstract functions of `RailwayCar`:

    class PassengerCar : RailwayCar {
        override void load() {
            writeln("The passengers are getting on");
        }

        override void unload() {
            writeln("The passengers are getting off");
        }
    }

    class FreightCar : RailwayCar {
        override void load() {
            writeln("The crates are being loaded");
        }

        override void unload() {
            writeln("The crates are being unloaded");
        }
    }

Being an abstract class does not preclude the use of `RailwayCar` in the program. Objects of `RailwayCar` can not be constructed but `RailwayCar` can be used as an interface. As the subclasses define the two relationships "passenger car is a railway car" and "freight car is a railway car", the objects of `PassengerCar` and `FreightCar` can be used in places of `RailwayCar`. This will be seen in the `Train` class below.

The class that represents a train can consist of a locomotive and an array of railwaycars:

    class Train : RailwayVehicle {
        Locomotive locomotive;
        RailwayCar[] cars;

        // ...
    }

I would like to repeat an important point: Although both `Locomotive` and `RailwayCar` inherit from `RailwayVehicle`, it would not be correct to inherit `Train` from either of them. Inheritance models the "is a" relationship and a train is neither a locomotive nor a passenger car. A train consists of them.

If we require that every train must have a locomotive, the `Train` constructor must ensure that it takes a valid `Locomotive` object. Similarly, if the railway cars are optional, they can be added by a member function:

    import std.exception;
    // ...

    class Train : RailwayVehicle {
        // ...

        this(Locomotive locomotive) {
            enforce(locomotive !is null,
                    "Locomotive cannot be null");
            this.locomotive = locomotive;
        }

        void addCar(RailwayCar[] cars...) {
            this.cars ~= cars;
        }

        // ...
    }

Note that `addCar()` can validate the `RailwayCar` objects as well. I am ignoring that validation here.

We can imagine that the departures and arrivals of trains should also be supported:

    class Train : RailwayVehicle {
        // ...

        void departStation(string station) {
            foreach (car; cars) {
                car.load();
            }

            writefln("Departing from %s station", station);
        }

        void arriveStation(string station) {
            writefln("Arriving at %s station", station);

            foreach (car; cars) {
                car.unload();
            }
        }
    }

The following `main()` is making use of the `RailwayVehicle` hierarchy:

    import std.stdio;

    // ...

    void main() {
        auto locomotive = new Locomotive;
        auto train = new Train(locomotive);

        train.addCar(new PassengerCar, new FreightCar);

        train.departStation("Ankara");
        train.advance(500);
        train.arriveStation("Haydarpaşa");
    }

The `Train` class is being used by functions that are provided by two separate interfaces:

1. When the `advance()` function is called, the `Train` object is being used as a `RailwayVehicle` because that function is declared by `RailwayVehicle`.
2. When the `departStation()` and `arriveStation()` functions are called, `train` is being used as a `Train` because those functions are declared by `Train`.

The arrows indicate that `load()` and `unload()` functions work according to the actual type of `RailwayCar`:

    The passengers are getting on     ←
    The crates are being loaded       ←
    Departing from Ankara station
    The vehicle is advancing 500 kilometers
    Arriving at Haydarpaşa station
    The passengers are getting off    ←
    The crates are being unloaded     ←

##### Summary

- Inheritance is used for the "is a" relationship.
- Every class can inherit from up to one `class`.
- `super` has two uses: Calling the constructor of the superclass and accessing the members of the superclass.
- `override` is for redefining member functions of the superclass specially for the subclass.
- `abstract` requires that a member function must be overridden.

##### Problemler

1. Let's modify `RailwayVehicle`. In addition to reporting the distance that it advances, let's have it also make sounds. To keep the output short, let's print the sounds per 100 kilometers:

       class RailwayVehicle {
           void advance(size_t kilometers) {
               writefln("The vehicle is advancing %s kilometers",
                        kilometers);

               foreach (i; 0 .. kilometers / 100) {
                   writefln("  %s", makeSound());
               }
           }

           // ...
       }

   However, `makeSound()` cannot be defined by `RailwayVehicle` because vehicles may have different sounds:

   - "choo choo" for `Locomotive`
   - "clack clack" for `RailwayCar`

   Note: Leave `Train.makeSound` to the next exercise.

   Because it must be overridden, `makeSound()` must be declared as `abstract` by the superclass:

       class RailwayVehicle {
           // ...

           abstract string makeSound();
       }

   Implement `makeSound()` for the subclasses and try the code with the following `main()`:

       void main() {
           auto railwayCar1 = new PassengerCar;
           railwayCar1.advance(100);

           auto railwayCar2 = new FreightCar;
           railwayCar2.advance(200);

           auto locomotive = new Locomotive;
           locomotive.advance(300);
       }

   Make the program produce the following output:

       The vehicle is advancing 100 kilometers
         clack clack
       The vehicle is advancing 200 kilometers
         clack clack
         clack clack
       The vehicle is advancing 300 kilometers
         choo choo
         choo choo
         choo choo

   Note that there is no requirement that the sounds of `PassengerCar` and `FreightCar` be different. They can share the same implemention from `RailwayCar`.

2. Think about how `makeSound()` can be implemented for `Train`. One idea is that `Train.makeSound` may return a `string` that consists of the sounds of the members of `Train`.

... çözümler

Copyright © 1999-2026 by the D Language Foundation | Page generated by Ddoc on Wed Aug 19 13:28:53 2026