Menu
Coddy logo textTech

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.

challenge icon

Défi

Facile

Construisons 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 abstraite PaymentProcessor qui sert de modèle pour toutes les méthodes de paiement. Cette classe doit comporter :
    • Un membre std::string accountId proté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 un std::string
    • Un destructeur virtuel
  • PaymentMethods.h : implémenter deux processeurs de paiement concrets qui héritent de PaymentProcessor :

    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"
  • main.cpp : lire quatre entrées (chacune sur une ligne distincte) :
    1. L’identifiant du compte de la carte bancaire
    2. Le pourcentage de frais de la carte bancaire (double)
    3. L’identifiant du compte bancaire
    4. Le nom de la banque

    Créer les deux processeurs de paiement et les stocker dans un tableau de pointeurs PaymentProcessor*. Traiter un paiement de 100.0 avec 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 $100

Comme 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;
}
quiz iconTestez-vous

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

Entraînez-vous par vous-même : Compilateur C++ en ligne