D/Przeładowywanie operatorów

< D

Wstęp edytuj

Przeładowywanie operatorów polega na zdefiniowaniu oczekiwanego zachowania dla zwykłych operatorów +, -, *, >> itd. Dzięki temu możemy napisać klasę obsługującą macierze (tablice dwuwymiarowe) z wygodnymi operacjami arytmetycznymi zarówno względem pojedynczych liczb (przeprowadzenie operacji na każdej komórce i podanej liczbie) jak i względem innych macierz, poprzez np. pomnożenie odpowiadających sobie komórek. Innym przykładem może być operator - do odwracania kąta w formacie stopnie-minuty-sekundy.

Operatory jednoargumentowe edytuj

Możemy przeładować łącznie 6 operatorów jednoargumentowych.

Operator Faktyczny zapis
-e e.opUnary!("-")()
+e e.opUnary!("+")()
~e e.opUnary!("~")()
*e e.opUnary!("*")()
++e e.opUnary!("++")()
--e e.opUnary!("--")()

Wspomniany we wstępie przykład z kątem (pomińmy fakt kątów ujemnych):

import std.stdio;
import std.format;

class coord
{
private:
    /* Kolejno stopnie, minuty i sekundy */
    int degrees;
    int minutes;
    int seconds;

public:
    this(int degrees, int minutes, int seconds)
    {
        this.degrees = degrees;
        this.minutes = minutes;
        this.seconds = seconds;
    }

    /* Próba wypisania danych */
    void toString(scope void delegate(const(char)[]) sink) const
    {
        writef("[%d, %d, %d]", degrees, minutes, seconds);
    }

    auto opUnary(string s)() if (s == "-")
    {
        return new coord(-degrees, -minutes, -seconds);
    }
}

void main()
{
    auto c = new coord(-20, 5, -12);

    writefln("%s", c);
    writefln("%s", -c);
}

Stdout:

[-20, 5, -12]
[20, -5, 12]

Jeśli nie rozumiesz fragmentu o wypisywaniu danych odsyłam tu.

Postinkrementacja i postdekrementacja edytuj

D nie pozwala na przeładowanie e-- i e++, więc w przypadku obiektów trzeba posługiwać się predekrementacją i preinkrementacją.

Operatory rzutowania edytuj

Operatorów rzutowania może być tyle na ile typów chcielibyśmy rzutować, więc w praktyce mamy do czynienia z tylko jednym sposobem:

Operator Faktyczny zapis
cast(typ) e e.opCast!(typ)()

Przykładem takiego rzutowania może być klasa sprawdzająca czy string wskazuje na prawdę, tj. 't' i 'T' (od true) to prawda, wszystko inne – fałsz.

import std.stdio;
import std.format;

/* Wspomniana klasa */
class isTrue
{
private:
    /* String na później */
    string str;

public:
    /* Konstruktor */
    this(string str)
    {
        this.str = str;
    }

    /* Operator rzutowania */
    bool opCast()
    {
        return (str == "y" || str == "Y");
    }
}

void main()
{
    /* Tablica obiektów klasy isTrue, sprawdzających *
     * „prawdziwość” przypisanych stringów           */
    isTrue[] arr;
    arr ~= new isTrue("y");
    arr ~= new isTrue("a");
    arr ~= new isTrue("tralala");
    arr ~= new isTrue("Y");
    arr ~= new isTrue("zupa");

    /* Iteracja po całej tablicy */
    foreach(isTrue a; arr)
        writefln("%s", cast(bool) a); //Wypisanie elementów po rzutowaniu na bool
}

Stdout:

true
false
false
true
false

Czyli wszystko się zgadza