C++ Klassen sortier- und streambar machen

Veröffentlicht am: Friday, May 8, 2020
Lesezeit: 3 Minuten Lesezeit
Tags: c++, tips

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 <iostream>
#include <vector>

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<SimpleInt> 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 <iostream>
#include <vector>
#include <algorithm>

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<SimpleInt> 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;
}