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. Du hast bereits gesehen, wie Lambdas mit std::transform verwendet werden. Sehen wir uns sowohl Funktoren als auch Lambda-Ausdrücke an – zwei Möglichkeiten, aufrufbare Objekte in C++ zu erstellen.
Ein Functor (Funktionsobjekt) ist eine Klasse, die den operator() überlädt, sodass 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));
// result: {3, 6, 9, 12}
}Jede Funktorinstanz kann über bei der Konstruktion gesetzte Membervariablen ihren eigenen unabhängigen Zustand enthalten – etwas, das ein gewöhnlicher Funktionsaufruf nicht kann. Die Definition einer Klasse für einfache Operationen ist jedoch umständlich. Lambda-Ausdrücke bieten eine kompakte 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 legt fest, auf welche Variablen außerhalb die Lambda-Funktion zugreifen kann. Verwende [=], um alle Variablen per Wert zu erfassen, [&], um alle per Referenz zu erfassen, oder liste bestimmte Variablen wie [factor] oder [&factor] auf.
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); });
// Sortiert nach Absolutwert: {-1, -2, 5, 8}Aufgabe
EinfachErstellen wir einen Preisrechner, der sowohl Funktoren als auch Lambda-Ausdrücke demonstriert, um verschiedene Rabattstrategien auf Produktpreise anzuwenden.
Du organisierst deinen Code über zwei Dateien:
Discounts.h: Definiere hier deine Rabattfunktoren und Hilfsfunktionen.Erstelle einen Funktor namens
PercentageDiscount, der einen Rabattprozentsatz (als Ganzzahl) speichert. Seinoperator()soll einendouble-Preis entgegennehmen und den reduzierten Preis zurückgeben. Beispielsweise sollte ein Rabatt von 20 % auf 100 $ den Wert 80 $ zurückgeben.Erstelle einen weiteren Funktor namens
FixedDiscount, der einen festen abzuziehenden Betrag (alsdouble) speichert. Seinoperator()soll einen Preis entgegennehmen und den Preis abzüglich des festen Betrags zurückgeben (jedoch niemals einen Wert unter 0).Erstelle eine Funktion namens
printPrices, die einenconst std::vector<double>&entgegennimmt und alle Preise durch Leerzeichen getrennt ausgibt, gefolgt von einem Zeilenumbruch. Formatiere jeden Preis mit zwei Dezimalstellen.main.cpp: Lies fünf Eingaben ein (jede in einer eigenen Zeile):- Preis des ersten Produkts (double)
- Preis des zweiten Produkts (double)
- Preis des dritten Produkts (double)
- Anzuwendender prozentualer Rabatt (Ganzzahl, z. B. 20 für 20 %)
- Fester Rabattbetrag (double)
Erstelle einen Vektor mit den drei Preisen und demonstriere beide Ansätze:
- Gib
Original prices:gefolgt von den Preisen aus - Verwende
std::transformmit deinemPercentageDiscount-Funktor, um einen neuen Vektor mit reduzierten Preisen zu erstellen. GibAfter percentage discount:gefolgt von den Ergebnissen aus - Verwende
std::transformmit deinemFixedDiscount-Funktor auf den ursprünglichen Preisen, um einen weiteren Vektor zu erstellen. GibAfter fixed discount:gefolgt von den Ergebnissen aus - Verwende
std::transformmit einem Lambda-Ausdruck, der jeden ursprünglichen Preis verdoppelt. GibPremium prices (doubled):gefolgt von den Ergebnissen aus - Verwende
std::sortmit einem Lambda, um die ursprünglichen Preise absteigend zu sortieren. GibSorted (high to low):gefolgt von den sortierten Preisen aus
Beispielsweise 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 Aufgabe ermöglicht dir, Funktoren (die ihren Zustand wie den Rabattbetrag beibehalten) mit Lambdas (die Variablen für schnelle, inline ausgeführte Operationen erfassen) zu vergleichen. Beide Ansätze funktionieren nahtlos mit STL-Algorithmen wie std::transform und std::sort.
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;
// Erstelle einen Vektor mit den drei Preisen
std::vector<double> prices = {price1, price2, price3};
// TODO: Print "Original prices:" followed by the prices using printPrices
// TODO: Use std::transform with PercentageDiscount functor
// Erstelle einen neuen Vektor für die Ergebnisse
// Print "After percentage discount:" followed by the results
// TODO: Use std::transform with FixedDiscount functor on original prices
// Erstelle einen neuen Vektor für die Ergebnisse
// Print "After fixed discount:" followed by the results
// TODO: Use std::transform with a lambda that doubles each original price
// Erstelle einen neuen Vektor für die Ergebnisse
// Print "Premium prices (doubled):" followed by the results
// TODO: Use std::sort with a lambda to sort original prices in descending order
// Print "Sorted (high to low):" followed by the sorted prices
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 & QuelldateienNamespaces & GültigkeitsbereichEinführung in OOP mit C++Klassen vs. ObjekteDer „this“-ZeigerMethoden (Member-Funktionen)Attribute (Datenmember)Grundlagen zu Ctors & DtorsRückblick – Einfacher Taschenrechner4Klassenattribute
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen und -KlassenZusammenfassung – Bankkontoverwaltung7Vererbung
Grundlagen der VererbungZugriffsebenen bei der VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick – Mitarbeiterhierarchie10STL-Übersicht
STL-Übersicht & PhilosophieSTL-ContainerIteratorenSTL-AlgorithmenFunktoren & Lambda-AusdrückeRückblick – Worthäufigkeit2Speicherverwaltung
Stack- vs. Heap-SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Intelligente Zeiger in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsbezeichner in C++Zugriffsbezeichner im DetailInformationsverbergungStruct vs. KlasseVerschachtelte und innere KlassenWiederholung – Studierendenverwaltungssystem8Polymorphie
Polymorphie zur Compilezeit vs. LaufzeitFunktionsüberladungVirtuelle Funktionen erneut betrachtetRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIRückblick – Shape-Calculator3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorMove-KonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktor im DetailDrei-/Fünf-/Null-RegelZusammenfassung – String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladung der Operatoren [] und ()TypkonvertierungsoperatorenWiederholung – Matrix-Klasse9Templates
FunktionstemplatesKlassen-TemplatesTemplate-SpezialisierungVariadische TemplatesGrundlagen zu SFINAE und Type TraitsRückblick – Generischer ContainerÜbe selbstständig: Online-C++-Compiler