Standard Template Library
Probeklausur SoSe 2023
Teilaufgabe 1.
Schreiben Sie einen Functor, der zu dem folgenden (und ähnlichen) Programmfragment kompatibel ist, sodass jedes Element des Vektors durch eine gegebene Zahl geteilt wird (im Beispiel 6).
Der Functor sollte außerdem überprüfen, ob eine Division durch Null erfolgt und in diesem Fall einen Exception vom Typ invalid_argument werfen.
std::vector <int > numbers_1 = {12, 24, 36, 48, 60, 72, 84, 96};
try {
Divide divideBySix (6);
std::for_each(numbers_1. begin(), numbers_1.end(), divideBySix);
} catch (std:: invalid_argument& e) {
std::cout << e.what() << std::endl;
}Antwort:
class Divide {
private:
int divisor;
public:
Divide(int d) : divisor(d) {}
int operator()(int x) const {
if (divisor == 0) throw std::invalid_argument("Division by zero");
return x / divisor;
}
};Teilaufgabe 2.
Schreiben Sie ein Predicate, das zu dem folgenden (und ähnlichen) Programmfragment kompatibel ist, sodass der letzte Wert im Vektor zurückgeliefert wird, der eine Quadratzahl ist.
std::vector <int > numbers_2 = {25, 36, 49, 64, 81, 100, 121, 143};
IsSquareNumber isSquareNumber;
std::vector<int>::reverse_iterator it2
= std::find_if(numbers_2.rbegin(), numbers_2.rend(), isSquareNumber);Hinweis: Sie dürfen für die Berechnung double sqrt(double x) der <cmath>-Bibliothek verwenden.
Antwort:
class IsSquareNumber {
public:
bool operator()(int x) const {
return std::sqrt(x) == static_cast<int>(std::sqrt(x));
}
};Tipps
Container
STL-Container speichern Daten in vordefinierten Strukturen. Die häufig verwendeten Container in den Aufgaben sind:
-
std::vector<T>
Dynamisch wachsende Sequenz, ideal, wenn du eine Liste von Elementen (z. B. Zahlen, Strings oder eigene Objekte) verwalten willst.
Beispiel:#include <vector> std::vector<int> numbers = {1, 2, 3, 4, 5}; -
std::map<Key, T>
Assoziatives Container, der Schlüssel-Wert-Paare speichert. Er ist sortiert nach den Schlüsseln.
Beispiel:#include <map> std::map<std::string, int> wordFrequency; wordFrequency["hello"] = 3; -
std::pair<T1, T2>
Hilfsklasse, um zwei zusammengehörige Werte zu speichern. Wird häufig in Kombination mit Container verwendet (z. B. beistd::map).
Beispiel:#include <utility> std::pair<std::string, int> entry("hello", 3);
Algorithmen und Iteratoren
STL bietet eine Vielzahl von Algorithmen, die du mit den Container-Iterators verwenden kannst. Einige Beispiele:
-
std::for_each
Führt eine Funktion (oder einen Funktor bzw. Lambda) für jedes Element in einem Bereich aus.
Beispiel:#include <algorithm> #include <vector> #include <iostream> class Divide { int divisor; public: Divide(int d) : divisor(d) {} void operator()(int &x) const { if(divisor == 0) throw std::invalid_argument("Division by zero"); x = x / divisor; } }; int main() { std::vector<int> numbers = {12, 24, 36, 48}; try { Divide divideBySix(6); std::for_each(numbers.begin(), numbers.end(), divideBySix); } catch(const std::invalid_argument& e) { std::cout << e.what() << std::endl; } return 0; }Beachte, dass in diesem Beispiel der Funktor so implementiert werden kann, dass er das Element modifiziert (daher als Parameter oft per Referenz übergeben wird).
-
std::find_if
Sucht in einem Bereich das erste Element, für das ein Prädikattruezurückgibt.
Beispiel:#include <algorithm> #include <vector> #include <cmath> class IsSquareNumber { public: bool operator()(int x) const { double root = std::sqrt(x); return root == static_cast<int>(root); } }; int main() { std::vector<int> numbers = {25, 36, 49, 64, 81, 100, 121, 143}; auto it = std::find_if(numbers.rbegin(), numbers.rend(), IsSquareNumber()); if (it != numbers.rend()) { std::cout << "Gefunden: " << *it << std::endl; } return 0; }Hier wird ein Reverse-Iterator verwendet, um vom Ende des Vektors nach vorne zu suchen.
-
std::sort
Sortiert Elemente in einem Container. Mit einem Lambda (oder Funktor) kann eine benutzerdefinierte Vergleichsfunktion angegeben werden.
Beispiel (aus der Wortzählung):#include <vector> #include <map> #include <algorithm> #include <iostream> // Annahme: wordFrequency ist ein map<string, int> std::map<std::string, int> wordFrequency = {{"der", 4}, {"Hund", 3}, {"springt", 2}}; std::vector<std::pair<std::string, int>> wordVector(wordFrequency.begin(), wordFrequency.end()); // Absteigend nach Häufigkeit sortieren: std::sort(wordVector.begin(), wordVector.end(), [](const auto &a, const auto &b) { return a.second > b.second; }); for (const auto& entry : wordVector) { std::cout << entry.first << ": " << entry.second << std::endl; } -
Iteratoren (z. B.
begin(),end(),rbegin(),rend())
Diese ermöglichen es dir, über die Container zu iterieren – auch in umgekehrter Reihenfolge (reverse iterator). Iteratoren können mitstd::sortverwendet werden, um die Elemente zu sortieren.
Funktoren (Function Objects)
- Funktoren:
Dies sind Klassen, die den Funktionsaufrufsoperatoroperator()überladen. Sie erlauben es, einen Zustand (zum Beispiel den Divisor) zu speichern.
Beispiel Divide:class Divide { int divisor; public: Divide(int d) : divisor(d) {} int operator()(int x) const { if(divisor == 0) throw std::invalid_argument("Division by zero"); return x / divisor; } };
<cmath>
std::sqrt(x): Berechnet die Quadratwurzel vonx.std::pow(x, y): Berechnetxhochy.std::sin(x),std::cos(x),std::tan(x): Berechnen die Sinus, Cosinus und Tangens vonx.std::log(x),std::log10(x): Berechnen die natürliche und dekadische Logarithmus vonx.std::exp(x): Berechnet die Exponentialfunktion vonx.std::fabs(x): Berechnet den Absolutwert vonx.