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