Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. C++
  3. Programmation orientée objet
Extrait - C++ Des fondamentaux du langage aux applications (3e édition)
Extraits du livre
C++ Des fondamentaux du langage aux applications (3e édition) Revenir à la page d'achat du livre

Programmation orientée objet

Classes et instances

L’objectif poursuivi par Bjarne Stroustrup était, rappelons-le, d’implémenter sur un compilateur C les classes décrites par le langage Simula. Ces deux derniers langages étant radicalement opposés dans leur approche, il fallait identifier une double continuité, notamment du côté du C.

Il fut aisé de remarquer que la programmation serait grandement simplifiée si certaines fonctions pouvaient migrer à l’intérieur des structures du C. De ce fait, il n’y aurait plus de structure à passer à ces fonctions puisqu’elles s’appliqueraient évidemment aux champs de la structure.

Toutefois, il fallait conserver un moyen de distinguer deux instances et c’est pour cette raison que l’on a modifié la syntaxe de l’opérateur point :

Programmation fonctionnelle

Programmation orientée objet

struct Point 
{ 
int x,y;
};
void afficher(Point p)
{ 
 printf("%d,%d\n",p.x,p.y); 
} 

struct Point 
{
int x,y; 
 void afficher() 
{ 
printf("%d,%d\n",x,y); 
} 
}; 

Point p1; 
afficher(p1); 

Point p1; 
p1.afficher(); 

Cette différence d’approche a plusieurs conséquences positives pour la programmation. Pour commencer, le programmeur n’a plus à effectuer un choix parmi les différents modes de passage de la structure à la fonction afficher(). Ensuite, nous allons pouvoir opérer une distinction entre les éléments (champs, fonctions) de premier plan et de second plan. Ceux de premier plan seront visibles, accessibles à l’extérieur de la structure. Les autres seront cachés, inaccessibles.

Ce procédé garantit une grande indépendance dans l’implémentation d’un concept, ce qui induit également une bonne stabilité des développements.

1. Définition de classe

Une classe est donc une structure possédant à la fois des champs et des fonctions. Lorsque les fonctions sont considérées à l’intérieur d’une classe, elles reçoivent le nom de méthodes.

L’ensemble des champs et des méthodes est désigné sous le terme de membres. Nous ne recommandons pas la désignation...

Héritage

1. Dérivation de classe (héritage)

Maintenant que nous connaissons bien la structure et le fonctionnement d’une classe, nous allons rendre nos programmes plus génériques. Il est fréquent de décrire un problème général avec des algorithmes appropriés, puis de procéder à de petites modifications lorsqu’un cas similaire vient à être traité. 

La philosophie orientée objet consiste à limiter au maximum les macros, les inclusions, les modules. Cette façon d’aborder les choses présente de nombreux risques lorsque la complexité des problèmes vient à croître. La programmation orientée objet s’exprime plutôt sur un axe générique/spécifique bien plus adapté aux petites variations des données d’un problème. Des méthodes de modélisation s’appuyant sur UML peuvent vous guider pour construire des réseaux de classes adaptés aux circonstances d’un projet. Mais il faut également s’appuyer sur des langages supportant cette approche, et C++ en fait partie.

a. Exemple de dérivation de classe

Imaginons une classe Compte, composée des éléments suivants :

class Compte 
{ 
protected: 
  int numero;    // numéro du compte 
  double solde;    // solde du compte 
 
  static int num;  // variable utilisée pour calculer le prochain numéro 
  static int prochain_numero(); 
 
public: 
  char*titulaire;    // titulaire du compte 
 
  Compte(char*titulaire); 
  ~Compte(void); 
  void crediter(double montant); 
  bool debiter(double montant); 
  void relever(); 
}; 

Nous pouvons maintenant imaginer une classe CompteRemunere, spécialisant le fonctionnement de la classe Compte. Il est aisé de concevoir qu’un compte rémunéré admet globalement les mêmes opérations qu’un compte classique, son comportement étant légèrement modifié pour ce qui est de l’opération de crédit, puisqu’un intérêt...

Autres aspects de la POO

1. Conversion dynamique

a. Conversions depuis un autre type

Les constructeurs permettent de convertir des objets à partir d’instances (de valeurs) exprimées dans un autre type.

Prenons le cas de notre classe Chaine. Il serait intéressant de "convertir" un char* ou un char en chaîne :

#include <string.h> 
 
class Chaine 
{ 
private: 
  char*buffer; 
  int t_buf; 
  int longueur; 
public: 
  // un constructeur par défaut 
  Chaine() 
  { 
    t_buf=100; 
    buffer=new char[t_buf]; 
    longueur=0; 
  } 
  Chaine (int t_buf) 
  { 
    this->t_buf=t_buf; 
    buffer=new char[t_buf]; 
    longueur=0; 
  } 
 
Chaine(char c) 
  { 
    t_buf=1; 
    longueur=1; 
    buffer=new char[t_buf]; 
    buffer[0]=c; 
  } 
 
  Chaine(char*s) 
  { 
    t_buf=strlen(s)+1; 
    buffer=new char[t_buf]; 
    longueur=strlen(s); 
    strcpy(buffer,s); 
  }     
 
  void afficher() 
  { 
    for(int i=0; i<longueur; i++) 
      printf("%c",buffer[i]); 
    printf("\n"); 
  } 
} ; 
 
int main(int argc, char* argv[]) 
{ 
  // Ecriture 1 
  // Conversion par emploi explicite de constructeur 
  Chaine x; 
  x=Chaine("bonjour");  // conversion 
  x.afficher(); 
 
  // Ecriture 2 
  // Transtypage (cast) par coercition 
  Chaine y=(char*) "bonjour"; // conversion (cast) 
  y.afficher(); 
 
  // Ecriture 3 
  // Transtypage (cast), approche C++ 
  Chaine...

Travaux pratiques

1. Utilisation de l’héritage de classes dans l’interprète tiny-lisp

Dans l’interprète tiny-lisp, des classes dérivant de ScriptBox sont chargées de faire fonctionner le parser dans autant d’environnements :

  • La boîte à sable ou Sandbox est un environnement isolé où les entrées-sorties sont neutralisées (elles ne sont pas activées).

  • L’environnement ConsoleBox assure des entrées-sorties sur la console système. 

La classe de base est appelée ScriptBox et elle définit une méthode virtuelle init_events() :

/* 
        Environnement d'exécution générique 
*/ 
class ScriptBox 
{ 
public: 
        LexScan*lexScan; 
        Evaluator*parser; 
 
        ScriptBox(); 
        ~ScriptBox(); 
 
        virtual void init_events(); 
 
        void new_text(); 
        void set_text(string text); 
        void parse(); 
        bool has_errors(); 
        string get_errors(); 
        string get_text(); 
 
        bool set_debug(); 
}; 

Dans la version neutre, Sandbox, cette méthode n’est pas surchargée.

/* 
        Environnement d'exécution neutre 
*/ 
class Sandbox : 
        public ScriptBox 
{ 
public: 
        Sandbox(); 
        ~Sandbox(); 
}; 

Dans le cas de l’environnement Consolebox, la méthode est au contraire surchargée pour déclarer des événements de type entrées-sorties sur la console (écran, clavier).

/* 
        Environnement d'exécution Console 
*/ 
class Consolebox : 
        public ScriptBox 
{ 
public: ...