Fonctions virtuelles pures
Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy. Leçon 59 sur 104.
Une fonction virtuelle pure est une fonction virtuelle qui n’a aucune implémentation dans la classe de base. Elle est déclarée en attribuant = 0 à la déclaration de la fonction. Cela indique au compilateur que les classes dérivées doivent fournir leur propre implémentation.
class Shape {
public:
virtual double area() = 0; // Fonction virtuelle pure
virtual ~Shape() = default;
};Contrairement aux fonctions virtuelles ordinaires qui fournissent un comportement par défaut, les fonctions virtuelles pures définissent un contrat : toute classe dérivée concrète doit implémenter cette fonction pour pouvoir être instanciée. La classe de base déclare simplement ce qui doit être fait, et non comment.
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double area() override {
return 3.14159 * radius * radius;
}
};
class Rectangle : public Shape {
double width, height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() override {
return width * height;
}
};Circle et Rectangle doivent tous deux implémenter area(), car cette fonction est virtuelle pure dans Shape. Si une classe dérivée n’implémente pas toutes les fonctions virtuelles pures, elle devient elle aussi abstraite et ne peut pas être instanciée.
Les fonctions virtuelles pures sont essentielles lorsque la classe de base ne peut pas fournir d’implémentation par défaut pertinente. Toute forme possède une aire, mais il n’existe aucun moyen raisonnable de calculer « l’aire d’une forme générique » sans connaître le type de forme spécifique.
Défi
FacileConstruisons un système de traitement des paiements qui montre comment les fonctions virtuelles pures imposent un contrat entre différentes méthodes de paiement. Vous allez créer une classe de base abstraite qui définit ce que chaque processeur de paiement doit faire, puis implémenter des types de paiement concrets qui respectent ce contrat.
Vous allez organiser votre code sur trois fichiers :
PaymentProcessor.h: définir une classe abstraitePaymentProcessorqui sert de modèle pour toutes les méthodes de paiement. Cette classe doit comporter :- Un membre
std::string accountIdprotégé - Un constructeur qui initialise l’identifiant du compte
- Une méthode virtuelle pure
processPayment(double amount): chaque type de paiement doit l’implémenter différemment - Une méthode virtuelle pure
getProcessorName()qui renvoie unstd::string - Un destructeur virtuel
- Un membre
PaymentMethods.h: implémenter deux processeurs de paiement concrets qui héritent dePaymentProcessor:CreditCardProcessor:- Un membre privé
double feePercentage(le taux de frais de transaction) - Un constructeur prenant l’identifiant du compte et le pourcentage de frais
- Implémenter
processPayment()pour calculer les frais (amount * feePercentage / 100), puis afficher :Credit Card [<accountId>]: Charged $<amount> (Fee: $<fee>) - Implémenter
getProcessorName()pour renvoyer"CreditCard"
BankTransferProcessor:- Un membre privé
std::string bankName - Un constructeur prenant l’identifiant du compte et le nom de la banque
- Implémenter
processPayment()pour afficher :Bank Transfer [<accountId>] via <bankName>: Transferred $<amount> - Implémenter
getProcessorName()pour renvoyer"BankTransfer"
- Un membre privé
main.cpp: lire quatre entrées (chacune sur une ligne distincte) :- L’identifiant du compte de la carte bancaire
- Le pourcentage de frais de la carte bancaire (double)
- L’identifiant du compte bancaire
- Le nom de la banque
Créer les deux processeurs de paiement et les stocker dans un tableau de pointeurs
PaymentProcessor*. Traiter un paiement de100.0avec chaque processeur, en affichant le nom du processeur avant chaque transaction :Processing with <processorName>: <processPayment output>Afficher une ligne vide entre les processeurs. Libérer les objets alloués dynamiquement une fois terminé.
Par exemple, avec les entrées CC-4521, 2.5, BA-7890 et National Bank :
Processing with CreditCard:
Credit Card [CC-4521]: Charged $100 (Fee: $2.5)
Processing with BankTransfer:
Bank Transfer [BA-7890] via National Bank: Transferred $100Comme PaymentProcessor possède des fonctions virtuelles pures, vous ne pouvez pas l’instancier directement : seules les implémentations concrètes qui respectent le contrat peuvent être créées. Cela garantit que chaque méthode de paiement fournit sa propre logique de traitement spécifique.
Essayez vous-même
#include <iostream>
#include <string>
#include "PaymentMethods.h"
using namespace std;
int main() {
// Lire les entrées
string ccAccountId;
double feePercentage;
string bankAccountId;
string bankName;
getline(cin, ccAccountId);
cin >> feePercentage;
cin.ignore();
getline(cin, bankAccountId);
getline(cin, bankName);
// TODO: Créer un tableau de pointeurs PaymentProcessor* avec 2 éléments
// TODO: Créer les objets CreditCardProcessor et BankTransferProcessor
// et les stocker dans le tableau
// TODO: Parcourir le tableau et pour chaque processeur :
// 1. Print "Processing with <processorName>:"
// 2. Appeler processPayment avec le montant 100.0
// 3. Afficher une ligne vide entre les processeurs (pas après le dernier)
// TODO: Nettoyer les objets alloués dynamiquement
return 0;
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesGénération et compilation en C++Fichiers d’en-tête et fichiers sourceEspaces de noms et portéeIntroduction à la POO en C++Classes et objetsLe pointeur « this »Méthodes (fonctions membres)Attributs (membres de données)Bases des constructeurs et destructeursRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d’instance et statiquesAccesseurs et mutateursFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d’accès de l’héritageOrdre d’appel des constructeurs et destructeursRedéfinition des méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif : hiérarchie des employés10Vue d’ensemble de la STL
Vue d’ensemble et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - fréquence des mots13Modèles de conception, partie 1
Introduction aux modèles de conceptionModèle SingletonFabrique et fabrique abstraiteModèle BuilderModèle ObserverModèle Strategy2Gestion de la mémoire
Mémoire de pile ou de tasPointeurs et référencesMémoire dynamique (new/delete)Pointeurs intelligents en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d’accès en C++Spécificateurs d’accès en profondeurMasquage de l’informationStruct vs classeClasses imbriquées et internesRécapitulatif - Système de gestion des dossiers étudiants8Polymorphisme
Polymorphisme à la compilation vs à l’exécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d’interfaces en C++Conversion dynamique et RTTIRécapitulatif - Calculateur de formes3Constructeurs et destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d’initialisation des constructeursConstructeurs déléguésApprofondissement des destructeursRègle des trois / cinq / zéroRécapitulatif - Classe String6Surcharge des opérateurs
Introduction à la surcharge des opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l’opérateur d’affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif – Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation des templatesTemplates variadiquesBases de SFINAE et des traits de typesRécapitulatif - Conteneur génériqueEntraînez-vous par vous-même : Compilateur C++ en ligne