8 May 2020

C++ Klassen sortier- und streambar machen

Wenn man Klassen in C++ implementiert ist es häufig eine gute Idee sie für die Algorithmen der Standard Template Library

vorzubereiten.

Teil 1: Die Klasse sortierbar machen

Wir betrachten folgendes Beispiel:

#include 
#include 
class SimpleInt
{
public:
    SimpleInt() {}
    SimpleInt(int value): value(value) {}
    int getValue() const { return value; }
    void setValue(int value) { this->value = value; }
private:
    int value;
};
int main()
{
    std::vector values;
    values.push_back(5);
    values.push_back(1);
    values.push_back(4);
    values.push_back(2);
    values.push_back(3);
    for (auto &value : values) {
        std::cout << value.getValue() << std::endl;
    }
    return 0;
}

Natürlich kann man den Vector jederzeit über ein Lambda sortieren.

std::sort(values.begin(), values.end(),
              [](const SimpleInt &a, const SimpleInt &b)->bool
    {
        return a.getValue() < b.getValue();
    });

Aber bequemer ist es, wenn die Klasse selbst einen Standard hat, wie sie sortiert wird.

std::sort(values.begin(), values.end());

Dies zu erreichen ist einfach. std::sort, wie die meisten Algorithmen der STL benötigt nur den `<` operator

um die Reihenfolge zweier Objekte zu bestimmen.

bool operator <(const SimpleInt &a, const SimpleInt &b) 
{ 
    return a.getValue() < b.getValue();
}

Etwas schöner wird die Implementierung, wenn man die Funktion in der Klasse als `friend` markiert.

Unser Beispiel sieht jetzt folgendermaßen aus:

class SimpleInt
{
public:
    SimpleInt() {}
    SimpleInt(int value): value(value) {}
    int getValue() const { return value; }
    void setValue(int value) { this->value = value; }
    friend bool operator <(const SimpleInt &a, const SimpleInt &b);
private:
    int value;
};
bool operator <(const SimpleInt &a, const SimpleInt &b)
{
    return a.value < b.value;
}

`friend` Methoden einer Klasse sind nicht Teil der Klasse, können aber auf ihre privaten Felder zugreifen.

Teil 2: Die Klasse streambar machen

Häufig kommt es vor, das der Zustand von Objekten zu Debugzwecken in Logdateien geschrieben werden muss.

Auch hier reicht es einfach den `<<` operator zu überladen.

std::ostream& operator <<(std::ostream &os, const SimpleInt &a)
{
    os << a.value; return os;
}

Und ihn in der Klasse als `friend` zu markieren.

Wo wir vorher noch mit `getValue()` ein Feld des Objektes holen mussten, reicht es jetzt das Objekt selbst in den Outputstream zu schreiben.

std::cout << value << std::endl;

Unser gesamtes Beispiel sieht jetzt folgendermaßen aus:

#include 
#include 
#include 
class SimpleInt
{
public:
    SimpleInt() {}
    SimpleInt(int value): value(value) {}
    int getValue() const { return value; }
    void setValue(int value) { this->value = value; }
    friend bool operator <(const SimpleInt &a, const SimpleInt &b);
    friend std::ostream& operator <<(std::ostream &os, const SimpleInt &a);
private:
    int value;
};
bool operator <(const SimpleInt &a, const SimpleInt &b)
{
    return a.value < b.value;
}
std::ostream& operator <<(std::ostream &os, const SimpleInt &a)
{
    os << a.value; return os;
}
int main()
{
    std::vector values;
    values.push_back(5);
    values.push_back(1);
    values.push_back(4);
    values.push_back(2);
    values.push_back(3);
    std::sort(values.begin(), values.end());
    for (auto &value : values) {
        std::cout << value << std::endl;
    }
    return 0;
}

Blogroll

Online Services

Neocities Eine moderne Version von Geocities. Manches Gem zu finden.
Gemini Protokolldefinition Eine moderne Version von Gopher
Posteo Ein sehr guter und sympathsicher Berliner Email Service.

Retro Computing

CBBS Outpost Aktuelle Liste von C64/C128 BBS Servern