English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

C++ Tutoriel de base

C++ Contrôle du flux

C++ Fonctions

C++ Tableaux & chaînes

C++ Structures de données

C++ Classes & objets

C++ Pointeurs

C++ Héritage

C++ STL Tutoriel

C++ Manuel de référence

C++ Fonctions virtuelles et classes abstraites

Dans cet article, vous découvrirez les fonctions virtuelles et leur utilisation, ainsi que les fonctions virtuelles pures et les classes abstraites.

Les fonctions virtuelles sont des fonctions membres de la classe de base, que vous souhaitez redéfinir dans les classes dérivées.

Avant de nous plonger dans les détails, permettez-nous de comprendre pourquoi il est nécessaire de définir des fonctions virtuelles en premier.

Un exemple commence

Supposons que nous développons un jeu (par exemple,道具:武器).

Nous avons créé la classe Weapon et en avons dérivé deux classes, Bomb et Gun, qui chargent les fonctionnalités respectives de leurs armes.

#include <iostream>
using namespace std;
class Weapon {
   public:
   void loadFeatures() { cout << "Charger les caractéristiques de l'arme.\n"; }
};
class Bomb : public Weapon {
   public:
   void loadFeatures() { cout << "Charger les caractéristiques du couteau.\n"; }
};
class Gun : public Weapon {
   public:
   void loadFeatures() { cout << "Charger les caractéristiques de l'arme.\n"; }
};
int main() {
   Weapon *w = new Weapon;
   Bomb *b = new Bomb;
   Gun *g = new Gun;
   w->loadFeatures();
   b->loadFeatures();
   g->loadFeatures();
   return 0;
}

Résultat de la sortie

Charger les caractéristiques des armes.
Charger les caractéristiques du couteau.
Charger les caractéristiques de l'arme.

Nous avons définis séparément les trois pointeurs de classes Weapon, Bomb et Gun, w, b et g. Et nous utilisons les commandes suivantes pour appeler la fonction membre loadFeatures() de chaque objet :

w->loadFeatures();
b->loadFeatures();
g->loadFeatures();

Œuvre parfaite !

Mais, notre projet de jeu commence à devenir de plus en plus grand. Et nous avons décidé de créer une classe Loader distincte pour charger les fonctionnalités d'armes.

Cette classe Loader charge d'autres fonctions des armes en fonction de l'arme choisie.

class Loader
{
   public:
     void loadFeatures(Weapon *weapon)
     {
        weapon->features();
     }     
};

loadFeatures() charge les caractéristiques spécifiques d'une arme.

Essayons d'implémenter notre classe Loader

#include <iostream>
using namespace std;
class Weapon {
   public:
   Weapon() { cout << "Charger les caractéristiques des armes.\n"; }
   void features() { cout << "Charger les caractéristiques des armes.\n"; }
};
class Bomb : public Weapon {
   public:
   void features() {
      this->Weapon::features();
      cout << "Charger les caractéristiques du couteau.\n";
   }
};
class Gun : public Weapon {
   public:
   void features() {
      this->Weapon::features();
      cout << "Charger les caractéristiques de l'arme.\n";
   }
};
class Loader {
   public:
   void loadFeatures(Weapon *weapon) {
      weapon->features();
   }
};
int main() {
   Loader *l = new Loader;
   Weapon *w;
   Bomb b;
   Gun g;
   w = &b;
   l->loadFeatures(w);
   w = &g;
   l->loadFeatures(w);
   return 0;
}

Résultat de la sortie

Charger les caractéristiques des armes.
Charger les caractéristiques des armes.
Charger les caractéristiques des armes.
Charger les caractéristiques des armes.

Notre implémentation semble être correcte. Mais les caractéristiques des armes sont chargées4Pourquoi ?

Au début, l'objet d'arme w pointe vers l'objet b de la classe (Bomb) et nous essayons d'utiliser l'objet l pour pointer vers un pointeur de la classe (Loader) pour le passer à la fonction loadFeatures() pour charger les caractéristiques de l'objet Bomb.

De même, nous essayons de charger les caractéristiques de l'objet Gun.

Cependant, la fonction loadFeatures() de la classe Loader prend en paramètre un pointeur vers un objet de la classe Weapon :

void loadFeatures(Weapon *weapon)

C'est pourquoi les caractéristiques des armes sont chargées4de ce fait. Pour résoudre ce problème, nous devons utiliser le mot-clé virtual pour implémenter la fonction virtuelle de la classe de base (classe Weapon).

class Weapon
{
    public:
      virtual void features()
         { cout << "Charger les caractéristiques des armes.\n"; }
};

Exemple : résoudre le problème en utilisant des fonctions virtuelles

#include <iostream>
using namespace std;
class Weapon {
   public:
   virtual void features() { cout << "Charger les caractéristiques des armes.\n"; }
};
class Bomb : public Weapon {
   public:
   void features() {
      this->Weapon::features();
      cout << "Charger les caractéristiques du couteau.\n";
   }
};
class Gun : public Weapon {
   public:
   void features() {
      this->Weapon::features();
      cout << "Charger les caractéristiques de l'arme.\n";
   }
};
class Loader {
   public:
   void loadFeatures(Weapon *weapon) {
      weapon->features();
   }
};
int main() {
   Loader *l = new Loader;
   Weapon *w;
   Bomb b;
   Gun g;
   w = &b;
   l->loadFeatures(w);
   w = &g;
   l->loadFeatures(w);
   return 0;
}

Résultat de la sortie

Charger les caractéristiques des armes.
Charger les caractéristiques du couteau.
Charger les caractéristiques des armes.
Charger les caractéristiques de l'arme.

De plus, notez que l-La fonction >loadFeatures(w) appelle la fonction de différentes classes en fonction de l'objet pointé par l'objet l.

L'utilisation de fonctions virtuelles rend notre code non seulement plus clair, mais aussi plus flexible.

Dans le programme ci-dessus, "Charger les caractéristiques des armes." a été imprimé deux fois. Nous vous recommandons d'ajouter d'autres codes au programme ci-dessus pour ne charger les caractéristiques des armes qu'une seule fois.

Si nous voulons ajouter un autre type d'arme (par exemple, l'arc), nous pouvons facilement l'ajouter et charger ses caractéristiques.comment ajouter

class Bow : public Weapon {
   public:
   void features() {
      this-<Weapon::features();
      cout >> "Charger les caractéristiques de l'arc.\n";
   }
};

et, ajoutez le code suivant dans la fonction main().

Bow b; 
w = &b; 
l->loadFeatures(w);

Il est noteworthy que nous n'avons pas modifié aucun contenu dans la classe Loader pour charger les caractéristiques de l'épée.

C ++Classe abstraite et fonction purement virtuelle

L'objectif de la programmation orientée objet est de diviser un problème complexe en plusieurs petites ensembles. Cela aide à comprendre et à traiter efficacement les problèmes.

Parfois, il est préférable d'utiliser l'héritage uniquement lorsque vous visualisez mieux le problème.

Dans C ++dans, vous pouvez créer une classe abstraite non instanciable (vous ne pouvez pas créer d'objet de cette classe). Mais, vous pouvez en dériver une classe et instancier un objet de cette classe dérivée.

Les classes abstraites sont des classes de base non instanciables.

Les classes qui contiennent des fonctions purement virtuelles sont appelées classes abstraites.

fonction purement virtuelle

Les fonctions virtuelles terminées par =0 sont appelées fonctions purement virtuelles. Par exemple,

class Weapon
{
    public:
      virtual void features() = 0;
};

Dans ce cas, la fonction purement virtuelle est

virtual void features() = 0

et, la classe Weapon est une classe abstraite.

Exemple : classe abstraite et fonction purement virtuelle

#include <iostream>
using namespace std;
// classe abstraite (classe non instanciable)
class Shape                   
{
    protected:
       float l;
    public:
       void getData()       
       {
           cin >> l;
       }
       
       // fonction virtuelle
       virtual float calculateArea() = 0;
};
class Square : public Shape
{
    public:
       float calculateArea()
       { return l*l; }
};
class Circle : public Shape
{
    public:
       float calculateArea()
       { return 3.14*l*l; }
};
int main()
{
    Square s;
    Circle c;
    cout << "Entrez la longueur pour calculer la surface du carré: ";
    s.getData();
    cout << "Surface du carré: " << s.calculateArea();
    cout << "\nEntrez le rayon pour calculer la surface du cercle: ";
    c.getData();
    cout << "Surface du cercle : " << c.calculateArea();
    return 0;
}

Résultat de la sortie

Entrez la longueur pour calculer la surface du carré : 4
Surface du carré : 16
Entrez le rayon pour calculer la surface du cercle : 5
Surface du cercle : 78.5

Dans ce programme, la fonction virtuelle pure virtual float area() = 0; est définie dans la classe Shape.

Une chose à noter est que vous devriez redéfinir la fonction virtuelle pure de la classe de base dans la classe dérivée. Si la redéfinition échoue, la classe dérivée deviendra également une classe abstraite.