Classes - 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 Bugzilla 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.

# Classes

Similar to structs, `class` is a feature for defining new types. By this definition, classes are user defined types. Different from structs, classes provide the object oriented programming (OOP) paradigm in D. The major aspects of OOP are the following:

- Encapsulation: Controlling access to members (Encapsulation is available for structs as well but it has not been mentioned until this chapter.)
- Inheritance: Acquiring members of another type
- Polymorphism: Being able to use a more special type in place of a more general type

Encapsulation is achieved by protection attributes, which we will see in a later chapter. Inheritance is for acquiring implementations of other types. Polymorphism is for abstracting parts of programs from each other and is achieved by class interfaces.

This chapter will introduce classes at a high level, underlining the fact that they are reference types. Classes will be explained in more detail in later chapters.

##### Comparing with structs

In general, classes are very similar to structs. Most of the features that we have seen for structs in the following chapters apply to classes as well:

- Structs
- Member Functions
- `const ref` Parameters and `const` Member Functions
- Constructor and Other Special Functions
- Operator Overloading

However, there are important differences between classes and structs.

###### Classes are reference types

The biggest difference from structs is that structs are value types and classes are reference types. The other differences outlined below are mostly due to this fact.

###### Class variables may be `null`

As it has been mentioned briefly in The `null` Value and the `is` Operator chapter, class variables can be `null`. In other words, class variables may not be providing access to any object. Class variables do not have values themselves; the actual class objects must be constructed by the `new` keyword.

As you would also remember, comparing a reference to `null` by the `==` or the `!=` operator is an error. Instead, the comparison must be done by the `is` or the `!is` operator, accordingly:

        MyClass referencesAnObject = new MyClass;
        assert(referencesAnObject !is null);

        MyClass variable;   // does not reference an object
        assert(variable is null);

The reason is that, the `==` operator may need to consult the values of the members of the objects and that attempting to access the members through a potentially `null` variable would cause a memory access error. For that reason, class variables must always be compared by the `is` and `!is` operators.

###### Class variables versus class objects

Class variable and class object are separate concepts.

Class objects are constructed by the `new` keyword; they do not have names. The actual concept that a class type represents in a program is provided by a class object. For example, assuming that a `Student` class represents students by their names and grades, such information would be stored by the members of `Student` objects. Partly because they are anonymous, it is not possible to access class objects directly.

A class variable on the other hand is a language feature for accessing class objects. Although it may seem syntactically that operations are being performed on a class variable, the operations are actually dispatched to a class object.

Let's consider the following code that we saw previously in the Value Types and Reference Types chapter:

        auto variable1 = new MyClass;
        auto variable2 = variable1;

The `new` keyword constructs an anonymous class object. `variable1` and `variable2` above merely provide access to that anonymous object:

     (anonymous MyClass object)    variable1    variable2
     ───┬───────────────────┬───  ───┬───┬───  ───┬───┬───
        │        ...        │        │ o │        │ o │
     ───┴───────────────────┴───  ───┴─│─┴───  ───┴─│─┴───
                  ▲                    │            │
                  │                    │            │
                  └────────────────────┴────────────┘

###### Copying

Copying affects only the variables, not the object.

Because classes are reference types, defining a new class variable as a copy of another makes two variables that provide access to the same object. The actual object is not copied.

Since no object gets copied, the postblit function `this(this)` is not available for classes.

        auto variable2 = variable1;

In the code above, `variable2` is being initialized by `variable1`. The two variables start providing access to the same object.

When the actual object needs to be copied, the class must have a member function for that purpose. To be compatible with arrays, this function may be named `dup()`. This function must create and return a new class object. Let's see this on a class that has various types of members:

    class Foo {
        S      o;  // assume S is a struct type
        char[] s;
        int    i;

    // ...

        this(S o, const char[] s, int i) {
            this.o = o;
            this.s = s.dup;
            this.i = i;
        }

        Foo dup() const {
            return new Foo(o, s, i);
        }
    }

The `dup()` member function makes a new object by taking advantage of the constructor of `Foo` and returns the new object. Note that the constructor copies the `s` member explicitly by the `.dup` property of arrays. Being value types, `o` and `i` are copied automatically.

The following code makes use of `dup()` to create a new object:

        auto var1 = new Foo(S(1.5), "hello", 42);
        auto var2 = var1.dup();

As a result, the objects that are associated with `var1` and `var2` are different.

Similarly, an `immutable` copy of an object can be provided by a member function appropriately named `idup()`. In this case, the constructor must be defined as `pure` as well. We will cover the `pure` keyword in a later chapter.

    class Foo {
    // ...
        this(S o, const char[] s, int i) pure {
            // ...

        }
        immutable(Foo) idup() const {
            return new immutable(Foo)(o, s, i);
        }
    }

    // ...

        immutable(Foo) imm = var1.idup();

###### Assignment

Just like copying, assignment affects only the variables.

Assigning to a class variable disassociates that variable from its current object and associates it with a new object.

If there is no other class variable that still provides access to the object that has been disassociated from, then that object is going to be destroyed some time in the future by the garbage collector.

        auto variable1 = new MyClass();
        auto variable2 = new MyClass();
        variable1 = variable2;

The assignment above makes `variable1` leave its object and start providing access to `variable2`'s object. Since there is no other variable for `variable1`'s original object, that object will be destroyed by the garbage collector.

The behavior of assignment cannot be changed for classes. In other words, `opAssign` cannot be overloaded for them.

###### Definition

Classes are defined by the `class` keyword instead of the `struct` keyword:

    class ChessPiece {
        // ...
    }

###### Construction

As with structs, the name of the constructor is `this`. Unlike structs, class objects cannot be constructed by the `{ }` syntax.

    class ChessPiece {
        dchar shape;

        this(dchar shape) {
            this.shape = shape;
        }
    }

Unlike structs, there is no automatic object construction where the constructor parameters are assigned to members sequentially:

    class ChessPiece {
        dchar shape;
        size_t value;
    }

    void main() {
        auto king = new ChessPiece('♔', 100);  // ← derleme HATASI
    }

    Error: no constructor for ChessPiece

For that syntax to work, a constructor must be defined explicitly by the programmer.

###### Destruction

As with structs, the name of the destructor is `~this`:

        ~this() {
            // ...
        }

However, different from structs, class destructors are not executed at the time when the lifetime of a class object ends. As we have seen above, the destructor is executed some time in the future during a garbage collection cycle. (By this distinction, class destructors should have more accurately been called finalizers).

As we will see later in the Memory Management chapter, class destructors must observe the following rules:

- A class destructor must not access a member that is managed by the garbage collector. This is because garbage collectors are not required to guarantee that the object and its members are finalized in any specific order. All members may have already been finalized when the destructor is executing.
- A class destructor must not allocate new memory that is managed by the garbage collector. This is because garbage collectors are not required to guarantee that they can allocate new objects during a garbage collection cycle.

Violating these rules is undefined behavior. It is easy to see an example of such a problem simply by trying to allocate an object in a class destructor:

    class C {
        ~this() {
            auto c = new C();    // ← WRONG: Allocates explicitly
                                 //          in a class destructor
        }
    }

    void main() {
        auto c = new C();
    }

The program is terminated with an exception:

    core.exception.InvalidMemoryOperationError@(0)

It is equally wrong to allocate new memory indirectly from the garbage collector in a destructor. For example, memory used for the elements of a dynamic array is allocated by the garbage collector as well. Using an array in a way that would require allocating a new memory block for the elements is undefined behavior as well:

        ~this() {
            auto arr = [ 1 ];    // ← WRONG: Allocates indirectly
                                 //          in a class destructor
        }

    core.exception.InvalidMemoryOperationError@(0)

###### Member access

Same as structs, the members are accessed by the dot operator:

        auto king = new ChessPiece('♔');
        writeln(king.shape);

Although the syntax makes it look as if a member of the variable is being accessed, it is actually the member of the object. Class variables do not have members, the class objects do. The `king` variable does not have a `shape` member, the anonymous object does.

Note: It is usually not proper to access members directly as in the code above. When that exact syntax is desired, properties should be preferred, which will be explained in a later chapter.

###### Operator overloading

Other than the fact that `opAssign` cannot be overloaded for classes, operator overloading is the same as structs. For classes, the meaning of `opAssign` is always associating a class variable with a class object.

###### Member functions

Although member functions are defined and used the same way as structs, there is an important difference: Class member functions can be and by-default are overridable. We will see this concept later in the Inheritance chapter.

As overridable member functions have a runtime performance cost, without going into more detail, I recommend that you define all `class` functions that do not need to be overridden with the `final` keyword. You can apply this guideline blindly unless there are compilation errors:

    class C {
        final int func() {    // ← Recommended
            // ...
        }
    }

Another difference from structs is that some member functions are automatically inherited from the `Object` class. We will see in the next chapter how the definition of `toString` can be changed by the `override` keyword.

###### The `is` and `!is` operators

These operators operate on class variables.

`is` specifies whether two class variables provide access to the same class object. It returns `true` if the object is the same and `false` otherwise. `!is` is the opposite of `is`.

        auto myKing = new ChessPiece('♔');
        auto yourKing = new ChessPiece('♔');
        assert(myKing !is yourKing);

Since the objects of `myKing` and `yourKing` variables are different, the `!is` operator returns `true`. Even though the two objects are constructed by the same character `'♔'`, they are still two separate objects.

When the variables provide access to the same object, `is` returns `true`:

        auto myKing2 = myKing;
        assert(myKing2 is myKing);

Both of the variables above provide access to the same object.

##### Summary

- Classes and structs share common features but have big differences.
- Classes are reference types. The `new` keyword constructs an anonymous class object and returns a class variable.
- Class variables that are not associated with any object are `null`. Checking against `null` must be done by `is` or `!is`, not by `==` or `!=`.
- The act of copying associates an additional variable with an object. In order to copy class objects, the type must have a special function likely named `dup()`.
- Assignment associates a variable with an object. This behavior cannot be changed.

Copyright © 1999-2026 by the D Language Foundation | Page generated by Ddoc on Mon Aug 17 18:57:12 2026