Funtori ed espressioni lambda
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 74 di 104.
Molti algoritmi STL accettano un oggetto chiamabile che personalizza il loro comportamento. Hai già visto le espressioni lambda usate con std::transform. Esploriamo sia i functor sia le espressioni lambda: due modi per creare oggetti chiamabili in C++.
Un funtore (oggetto funzione) è una classe che sovraccarica l'operator(), consentendo alle istanze di essere chiamate come funzioni:
#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}
}Ogni istanza di functor può mantenere un proprio stato indipendente tramite variabili membro impostate al momento della costruzione, cosa che una normale chiamata di funzione non può fare. Tuttavia, definire una classe per operazioni semplici è prolisso. Le espressioni lambda offrono un'alternativa concisa:
int factor = 3;
std::transform(nums.begin(), nums.end(), result.begin(),
[factor](int x) { return x * factor; });La sintassi lambda è [capture](parameters) { body }. La clausola di cattura specifica quali variabili esterne la lambda può accedere. Usa [=] per catturare tutto per valore, [&] per catturare tutto per riferimento oppure elenca variabili specifiche come [factor] o [&factor].
Le lambda sono particolarmente utili per operazioni occasionali con algoritmi come l’ordinamento con criteri personalizzati:
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); });
// Ordina per valore assoluto: {-1, -2, 5, 8}Sfida
FacileCostruiamo un calcolatore dei prezzi che mostri sia i funtori sia le espressioni lambda per applicare diverse strategie di sconto ai prezzi dei prodotti.
Organizzerai il codice in due file:
Discounts.h: qui definisci i funtori per gli sconti e le funzioni di utilità.Crea un funtore chiamato
PercentageDiscountche memorizzi una percentuale di sconto (come intero). Il suooperator()deve accettare un prezzodoublee restituire il prezzo scontato. Per esempio, uno sconto del 20% su $100 deve restituire $80.Crea un altro funtore chiamato
FixedDiscountche memorizzi un importo fisso da sottrarre (comedouble). Il suooperator()deve accettare un prezzo e restituire il prezzo meno l'importo fisso (senza mai scendere sotto 0).Crea una funzione chiamata
printPricesche accetti unconst std::vector<double>&e stampi tutti i prezzi separati da spazi, seguiti da una nuova riga. Formatta ogni prezzo con due cifre decimali.main.cpp: leggi cinque valori (ciascuno su una riga separata):- Prezzo del primo prodotto (double)
- Prezzo del secondo prodotto (double)
- Prezzo del terzo prodotto (double)
- Percentuale di sconto da applicare (intero, ad esempio 20 per il 20%)
- Importo dello sconto fisso (double)
Crea un vettore con i tre prezzi e mostra entrambi gli approcci:
- Stampa
Original prices:seguito dai prezzi - Usa
std::transformcon il tuo funtorePercentageDiscountper creare un nuovo vettore di prezzi scontati. StampaAfter percentage discount:seguito dai risultati - Usa
std::transformcon il tuo funtoreFixedDiscountsui prezzi originali per creare un altro vettore. StampaAfter fixed discount:seguito dai risultati - Usa
std::transformcon un'espressione lambda che raddoppi ogni prezzo originale. StampaPremium prices (doubled):seguito dai risultati - Usa
std::sortcon una lambda per ordinare i prezzi originali in ordine decrescente. StampaSorted (high to low):seguito dai prezzi ordinati
Per esempio, con gli input 100.00, 50.00, 75.00, 20 e 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 Questa sfida ti permette di confrontare i funtori (che mantengono uno stato, come l'importo dello sconto) con le lambda (che catturano variabili per operazioni rapide e inline). Entrambi gli approcci funzionano perfettamente con algoritmi STL come std::transform e std::sort.
Provalo tu
#include <iostream>
#include <vector>
#include <algorithm>
#include "Discounts.h"
int main() {
// Leggi gli input
double price1, price2, price3;
int percentageDiscount;
double fixedDiscount;
std::cin >> price1;
std::cin >> price2;
std::cin >> price3;
std::cin >> percentageDiscount;
std::cin >> fixedDiscount;
// Crea un vettore con i tre prezzi
std::vector<double> prices = {price1, price2, price3};
// TODO: Stampa "Original prices:" seguito dai prezzi usando printPrices
// TODO: Usa std::transform con il funtore PercentageDiscount
// Crea un nuovo vettore per i risultati
// Stampa "After percentage discount:" seguito dai risultati
// TODO: Usa std::transform con il funtore FixedDiscount sui prezzi originali
// Crea un nuovo vettore per i risultati
// Stampa "After fixed discount:" seguito dai risultati
// TODO: Usa std::transform con una lambda che raddoppia ogni prezzo originale
// Crea un nuovo vettore per i risultati
// Stampa "Premium prices (doubled):" seguito dai risultati
// TODO: Usa std::sort con una lambda per ordinare i prezzi originali in ordine decrescente
// Stampa "Sorted (high to low):" seguito dai prezzi ordinati
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti10Panoramica della STL
Panoramica e filosofia della STLContenitori STLIteratoriAlgoritmi STLFuntori ed espressioni lambdaRiepilogo - Frequenza delle parole2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online