C++-Standardbibliothek utility
In der C++-Standardbibliothek,<utility>Die Header-Datei enthält einige praktische Hilfsklassen und -funktionen, die beim Schreiben von effizientem und lesbarem Code sehr nützlich sind.
utilityDer zentrale Wert der Bibliothek liegt in:
- Bereitstellung grundlegender Datenstrukturen und Hilfsfunktionen
- Vereinfachung der Implementierung gängiger Programmieraufgaben
- Bereitstellung grundlegender Unterstützung für andere Komponenten der Standardbibliothek
utilityObwohl die Header-Datei klein ist, sind die Werkzeuge, die sie bereitstellt, sehr praktisch:
| Komponenten/Funktionen | Zweck | Anwendungsfall |
|---|---|---|
std::pair |
Zwei zusammengehörige Werte speichern | Funktionen, die mehrere Werte zurückgeben, Map-Elemente |
std::make_pair |
Bequemes Erstellen von Paaren | Template-Typableitung, Code vereinfachen |
std::swap |
Zwei Werte tauschen | Algorithmusimplementierung, Sortieroperationen |
std::move |
Move-Semantik aktivieren | Ressourcenverwaltung, Leistungsoptimierung |
std::forward |
Perfektes Forwarding | Universelle Referenzen, Template-Programmierung |
Kernkomponenten erklärt
std::pair: Schlüssel-Wert-Paar-Container
std::pairJautilitypair: die am häufigsten verwendete Komponente in der Bibliothek, mit der zwei Werte zu einem einzigen Objekt kombiniert werden.
Grundlegende Syntax
#include <utility> // 创建 pair 对象的基本方式 std::pair<类型1, 类型2> 变量名(值1, 值2);
Beispiel
#include <utility>
#include <string>
int main() {
// Methode 1: Direkte Initialisierung
std::pair<int, std::string> student1(101, "Alice");
// Methode 2: Verwendung der make_pair-Funktion (empfohlen)
auto student2 = std::make_pair(102, "Bob");
// Methode 3: Deduktionsleitfaden wird seit C++17 unterstützt
std::pair student3(103, "Charlie");
// Zugriff auf die Member von pair
std::cout << "Studenten-ID: " << student1.first << ", Name: " << student1.second << std::endl;
return 0;
}
Häufige Operationen mit pair
Beispiel
#include <iostream>
void pairOperations() {
// Ein pair erstellen
std::pair<int, double> p1(10, 3.14);
std::pair<int, double> p2(20, 2.71);
// Vergleichsoperationen
if (p1 < p2) {
std::cout << "p1 ist kleiner als p2" << std::endl;
}
// Zuweisungsoperation
p1 = p2;
// Elementweise Zuweisung seit C++11 unterstützt
int a;
double b;
std::tie(a, b) = p1; // Den Wert von p1 a bzw. b zuweisen
// C++17 strukturierte Bindung (prägnanter)
auto [x, y] = p1;
std::cout << "x = " << x << ", y = " << y << std::endl;
}
std::make_pair: praktische Erzeugungsfunktion
std::make_pairstd::make_pair: eine Template-Funktion, die Typen automatisch ableiten kann und das Erstellen von Paaren vereinfacht.
Beispiel
#include <iostream>
#include <string>
void demonstrateMakePair() {
// Automatische Typableitung, keine explizite Angabe von Template-Parametern nötig
auto p1 = std::make_pair(42, "Hello");
auto p2 = std::make_pair(3.14, true);
// Besonders praktisch für die Verwendung in Containern
std::vector<std::pair<int, std::string>> students;
students.push_back(std::make_pair(101, "Alice"));
students.push_back(std::make_pair(102, "Bob"));
for (const auto& student : students) {
std::cout << "Studenten-ID: " << student.first
<< ", Name: " << student.second << std::endl;
}
}
Hilfsfunktionen
std::swap: zwei Werte tauschen
std::swapWird verwendet, um die Werte zweier Objekte desselben Typs zu tauschen.
Beispiel
#include <iostream>
void demonstrateSwap() {
int a = 10, b = 20;
std::cout << "Vor dem Tausch: a = " << a << ", b = " << b << std::endl;
std::swap(a, b);
std::cout << "Nach dem Tausch: a = " << a << ", b = " << b << std::endl;
// Kann auch für benutzerdefinierte Typen verwendet werden (wenn Move-Semantik implementiert ist)
std::string str1 = "Hallo", str2 = "Welt";
std::swap(str1, str2);
std::cout << "String-Austausch: " << str1 << " " << str2 << std::endl;
}
std::move: Unterstützung für Move-Semantik
std::moveWird verwendet, um ein Objekt in eine rvalue-Referenz umzuwandeln und dadurch Move-Semantik zu ermöglichen.
Beispiel
#include <iostream>
#include <vector>
void demonstrateMove() {
std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2;
std::cout << "Vor dem Verschieben - v1 Größe: " << v1.size()
<< ", v2 Größe: " << v2.size() << std::endl;
// Move-Semantik verwenden, um Ressourcenbesitz zu übertragen
v2 = std::move(v1);
std::cout << "Nach dem Verschieben - v1 Größe: " << v1.size()
<< ", v2 Größe: " << v2.size() << std::endl;
// v1 befindet sich jetzt in einem gültigen, aber nicht spezifizierten Zustand
// Normalerweise sollte v1 nicht erneut verwendet werden, außer es wird neu zugewiesen
}
std::forward: perfekte Weiterleitung
std::forwardWird verwendet, um Perfect Forwarding zu implementieren und die Wertkategorie von Argumenten zu erhalten.
Beispiel
#include <iostream>
// Gewöhnliche Funktion – kann die Wertkategorie nicht beibehalten
template<typename T>
void normalFunction(T arg) {
std::cout << "Gewöhnlicher Funktionsparameter" << std::endl;
}
// Funktion, die Perfect Forwarding verwendet
template<typename T>
void perfectForwardingFunction(T&& arg) {
// Ursprüngliche Wertkategorie des Parameters beibehalten (lvalue oder rvalue)
normalFunction(std::forward<T>(arg));
}
void demonstrateForward() {
int x = 10;
// Übergib einen lvalue
perfectForwardingFunction(x);
// Übergebe einen rvalue
perfectForwardingFunction(20);
}
Dienstprogramme für Integer-Sequenzen (C++14)
C++14 führte Dienstprogramme für Integer-Sequenzen ein, die hauptsächlich für Template-Metaprogrammierung verwendet werden.
std::integer_sequence
Beispiel
#include <iostream>
// Verwende eine Integer-Sequenz, um jeden Wert in der Sequenz auszugeben
template<typename T, T... Ints>
void print_sequence(std::integer_sequence<T, Ints...>) {
// Verwende einen Fold-Ausdruck (C++17), um alle Werte auszugeben
((std::cout << Ints << " "), ...);
std::cout << std::endl;
}
void demonstrateIntegerSequence() {
// Erstelle eine Integer-Sequenz
auto seq = std::integer_sequence<int, 1, 2, 3, 4, 5>();
print_sequence(seq);
// Verwende make_integer_sequence, um die Sequenz zu erzeugen
auto seq2 = std::make_integer_sequence<int, 5>();
print_sequence(seq2); // Ausgabe: 0 1 2 3 4
}
Praxisnahe Anwendungsbeispiele
Beispiel 1: Funktion, die mehrere Werte zurückgibt
Beispiel
#include <iostream>
#include <cmath>
// Funktion gibt mehrere Werte zurück: Berechnungsergebnis und Fehlercode
std::pair<double, bool> calculateSqrt(double number) {
if (number < 0) {
return std::make_pair(0.0, false); // Fehlerfall
}
return std::make_pair(std::sqrt(number), true); // Erfolgsfall
}
void multipleReturnValues() {
auto result1 = calculateSqrt(16.0);
if (result1.second) {
std::cout << "Quadratwurzel: " << result1.first << std::endl;
} else {
std::cout << "Berechnungsfehler: Die Quadratwurzel einer negativen Zahl kann nicht gezogen werden" << std::endl;
}
auto result2 = calculateSqrt(-4.0);
if (!result2.second) {
std::cout << "Berechnungsfehler: Die Quadratwurzel einer negativen Zahl kann nicht gezogen werden" << std::endl;
}
}
Fall 2: Anwendung in STL-Containern
Beispiel
#include <map>
#include <iostream>
#include <string>
void mapWithPair() {
// Jedes Element von std::map ist ein std::pair
std::map<int, std::string> studentMap;
// Schlüssel-Wert-Paar einfügen
studentMap.insert(std::make_pair(101, "Alice"));
studentMap.emplace(102, "Bob"); // Effizienterer Weg
// Über die Map iterieren
for (const auto& [id, name] : studentMap) {
std::cout << "Studenten-ID: " << id << ", Name: " << name << std::endl;
}
// Element finden
auto it = studentMap.find(101);
if (it != studentMap.end()) {
std::cout << "Gefundener Student: " << it->second << std::endl;
}
}
Beispiel 3: Implementierung eines einfachen Wörterbuchs
Beispiel
#include <vector>
#include <iostream>
#include <algorithm>
class SimpleDictionary {
private:
std::vector<std::pair<std::string, std::string>> entries;
public:
void addWord(const std::string& word, const std::string& meaning) {
entries.emplace_back(word, meaning);
}
std::pair<bool, std::string> findMeaning(const std::string& word) {
for (const auto& [w, m] : entries) {
if (w == word) {
return std::make_pair(true, m);
}
}
return std::make_pair(false, "");
}
void printAll() {
for (const auto& [word, meaning] : entries) {
std::cout << word << ": " << meaning << std::endl;
}
}
};
void dictionaryExample() {
SimpleDictionary dict;
dict.addWord("Apfel", "eine Frucht");
dict.addWord("Buch", "ein Gegenstand zum Lesen");
auto result = dict.findMeaning("Apfel");
if (result.first) {
std::cout << "Bedeutung: " << result.second << std::endl;
}
}
Bewährte Methoden und Überlegungen
1. Verwenden Sie auto, um die Paarerstellung zu vereinfachen
Beispiel
auto student = std::make_pair(101, "Alice");
// Nicht empfohlen: den Typ explizit angeben (ausführlicher)
std::pair<int, std::string> student(101, "Alice");
2. Bevorzuge emplace gegenüber insert
Beispiel
// Empfohlen: emplace verwenden (effizienter)
myMap.emplace(1, "one");
// Nicht empfohlen: insert verwenden (erfordert das Erstellen eines temporären Objekts)
myMap.insert(std::make_pair(1, "one"));
3. Korrekte Verwendung der Move-Semantik
Beispiel
std::string str = "a very large string";
// Korrekt: move bei Rückgabe verwenden
return str; // Der Compiler optimiert automatisch, kein expliziter move nötig
}
void processString(std::string str) {
// Strings behandeln
}
void usageExample() {
std::string largeStr = "very large data";
// Korrekt: move beim Übergeben verwenden
processString(std::move(largeStr));
// Hinweis: largeStr sollte nicht mehr verwendet werden
}
4. Verwendung von strukturierten Bindungen
Beispiel
std::pair<int, std::string> getStudent() {
return {101, "Alice"};
}
void structuredBindingExample() {
// Traditionelle Methode
auto student = getStudent();
int id = student.first;
std::string name = student.second;
// Moderne Methode (C++17)
auto [id2, name2] = getStudent(); // Prägnanter und klarer
}