Funktoren & Lambda-Ausdrücke
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 74 von 104.
Viele STL-Algorithmen akzeptieren ein aufrufbares Objekt, das ihr Verhalten anpasst. Sie haben bereits gesehen, wie Lambdas mit std::transform verwendet werden. Lassen Sie uns sowohl Funktoren als auch Lambda-Ausdrücke untersuchen – zwei Möglichkeiten, aufrufbare Objekte in C++ zu erstellen.
Ein Funktor (Funktionsobjekt) ist eine Klasse, die den operator() überlädt, wodurch Instanzen wie Funktionen aufgerufen werden können:
#include <algorithm>
#include <vector>
#include <iostream>
struct MultiplyBy {
int factor;
MultiplyBy(int f) : factor(f) {}
int operator()(int x) const {
return x * factor;
}
};
int main() {
std::vector<int> nums = {1, 2, 3, 4};
std::vector<int> result(nums.size());
std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));
// Ergebnis: {3, 6, 9, 12}
}Funktoren können den Zustand zwischen Aufrufen beibehalten, was reguläre Funktionen nicht können. Das Definieren einer Klasse für einfache Operationen ist jedoch umständlich. Lambda-Ausdrücke bieten eine prägnante Alternative:
int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
[factor](int x) { return x * factor; });Die Lambda-Syntax lautet [capture](parameters) { body }. Die Capture-Klausel gibt an, auf welche äußeren Variablen das Lambda zugreifen kann. Verwenden Sie [=], um alle nach Wert zu erfassen, [&], um alle per Referenz zu erfassen, oder listen Sie spezifische Variablen auf wie [factor] oder [&factor].
Lambdas sind besonders nützlich für einmalige Operationen mit Algorithmen wie dem Sortieren nach benutzerdefinierten Kriterien:
std::vector<int> nums = {5, -2, 8, -1};
std::sort(nums.begin(), nums.end(),
[](int a, int b) { return std::abs(a) < std::abs(b); });
// Nach Absolutwert sortiert: {-1, -2, 5, 8}Aufgabe
EinfachLassen Sie uns einen Preisrechner erstellen, der sowohl Funktoren als auch Lambda-Ausdrücke demonstriert, um verschiedene Rabattstrategien auf Produktpreise anzuwenden.
Sie werden Ihren Code in zwei Dateien organisieren:
Discounts.h: Definieren Sie hier Ihre Rabatt-Funktoren und Hilfsfunktionen.Erstellen Sie einen Funktor namens
PercentageDiscount, der einen Rabattprozentsatz (als Ganzzahl) speichert. Seinoperator()sollte einendoublePreis entgegennehmen und den rabattierten Preis zurückgeben. Zum Beispiel sollte ein Rabatt von 20% auf 100 $ den Wert 80 $ zurückgeben.Erstellen Sie einen weiteren Funktor namens
FixedDiscount, der einen festen Betrag zum Abziehen (alsdouble) speichert. Seinoperator()sollte einen Preis entgegennehmen und den Preis abzüglich des festen Betrags zurückgeben (aber niemals weniger als 0).Erstellen Sie eine Funktion namens
printPrices, die einenconst std::vector<double>&entgegennimmt und alle Preise durch Leerzeichen getrennt ausgibt, gefolgt von einem Zeilenumbruch. Formatieren Sie jeden Preis mit zwei Dezimalstellen.main.cpp: Lesen Sie fünf Eingaben ein (jede in einer separaten Zeile):- Erster Produktpreis (double)
- Zweiter Produktpreis (double)
- Dritter Produktpreis (double)
- Anzuwendender prozentualer Rabatt (Ganzzahl, z. B. 20 für 20%)
- Fester Rabattbetrag (double)
Erstellen Sie einen Vektor mit den drei Preisen und demonstrieren Sie beide Ansätze:
- Geben Sie
Original prices:gefolgt von den Preisen aus - Verwenden Sie
std::transformmit IhremPercentageDiscount-Funktor, um einen neuen Vektor mit rabattierten Preisen zu erstellen. Geben SieAfter percentage discount:gefolgt von den Ergebnissen aus - Verwenden Sie
std::transformmit IhremFixedDiscount-Funktor auf den Originalpreisen, um einen weiteren Vektor zu erstellen. Geben SieAfter fixed discount:gefolgt von den Ergebnissen aus - Verwenden Sie
std::transformmit einem Lambda-Ausdruck, der jeden Originalpreis verdoppelt. Geben SiePremium prices (doubled):gefolgt von den Ergebnissen aus - Verwenden Sie
std::sortmit einem Lambda, um die Originalpreise in absteigender Reihenfolge zu sortieren. Geben SieSorted (high to low):gefolgt von den sortierten Preisen aus
Zum Beispiel mit den Eingaben 100.00, 50.00, 75.00, 20 und 15.00:
Original prices: 100.00 50.00 75.00
After percentage discount: 80.00 40.00 60.00
After fixed discount: 85.00 35.00 60.00
Premium prices (doubled): 200.00 100.00 150.00
Sorted (high to low): 100.00 75.00 50.00 Diese Herausforderung ermöglicht es Ihnen, Funktoren (die einen Zustand wie den Rabattbetrag beibehalten) mit Lambdas (die Variablen für schnelle Inline-Operationen erfassen) zu vergleichen. Beide Ansätze arbeiten nahtlos mit STL-Algorithmen wie std::transform und std::sort zusammen.
Spickzettel
STL-Algorithmen akzeptieren aufrufbare Objekte, um ihr Verhalten anzupassen. Zwei gängige Ansätze sind Funktoren und Lambda-Ausdrücke.
Ein Funktor ist eine Klasse, die den operator() überlädt, wodurch Instanzen wie Funktionen aufgerufen werden können:
struct MultiplyBy {
int factor;
MultiplyBy(int f) : factor(f) {}
int operator()(int x) const {
return x * factor;
}
};
std::transform(nums.begin(), nums.end(), result.begin(), MultiplyBy(3));Funktoren können einen Zustand zwischen Aufrufen beibehalten, erfordern jedoch ausführliche Klassendefinitionen.
Lambda-Ausdrücke bieten eine prägnante Alternative mit der Syntax [capture](parameters) { body }:
int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
[factor](int x) { return x * factor; });Die Capture-Klausel gibt an, auf welche externen Variablen das Lambda zugreifen kann:
[=]- alle per Wert erfassen (capture by value)[&]- alle per Referenz erfassen (capture by reference)[factor]- spezifische Variable per Wert erfassen[&factor]- spezifische Variable per Referenz erfassen
Lambdas eignen sich gut für Algorithmen wie std::sort für benutzerdefinierte Sortierkriterien:
std::sort(nums.begin(), nums.end(),
[](int a, int b) { return std::abs(a) < std::abs(b); });Probier es selbst
#include <iostream>
#include <vector>
#include <algorithm>
#include "Discounts.h"
int main() {
// Eingaben lesen
double price1, price2, price3;
int percentageDiscount;
double fixedDiscount;
std::cin >> price1;
std::cin >> price2;
std::cin >> price3;
std::cin >> percentageDiscount;
std::cin >> fixedDiscount;
// Einen Vektor mit den drei Preisen erstellen
std::vector<double> prices = {price1, price2, price3};
// TODO: "Original prices:" gefolgt von den Preisen mittels printPrices ausgeben
// TODO: std::transform mit dem PercentageDiscount Funktor verwenden
// Einen neuen Vektor für die Ergebnisse erstellen
// "After percentage discount:" gefolgt von den Ergebnissen ausgeben
// TODO: std::transform mit dem FixedDiscount Funktor auf die Originalpreise anwenden
// Einen neuen Vektor für die Ergebnisse erstellen
// "After fixed discount:" gefolgt von den Ergebnissen ausgeben
// TODO: std::transform mit einem Lambda verwenden, das jeden Originalpreis verdoppelt
// Einen neuen Vektor für die Ergebnisse erstellen
// "Premium prices (doubled):" gefolgt von den Ergebnissen ausgeben
// TODO: std::sort mit einem Lambda verwenden, um die Originalpreise in absteigender Reihenfolge zu sortieren
// "Sorted (high to low):" gefolgt von den sortierten Preisen ausgeben
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie10STL-Übersicht
STL-Übersicht & PhilosophieSTL-ContainerIteratorenSTL-AlgorithmenFunktoren & Lambda-AusdrückeRückblick – Wortfrequenz2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container