dimanche 14 décembre 2008

Le C orienté objet, 2ème partie

Un des concepts fondamentaux de la programmation orienté objet est l’héritage, comment l’implémenter en langage C ? Prenons deux classes, une classe mère « ClasseMère » et une classe fille « ClasseFille » qui hérite de la première. La classe mère içi sera une pale copie de la classe MonAutreClasse présentée dans la première partie.  Les fonctions concernant le descripteur de classe et le constructeur n’ont pas beaucoup changés. Pour les autres méthodes vous noterez l’usage de macro-fonctions dans lesquelles nous avons réécris les méthodes. Pour des raisons évidentes d’encapsulation, ces macros n’ont pas été mises dans le fichier d’en-tête  « classemere.h », elles n’ont pas non plus été mises dans le fichier « classemere.c » (afin d’éviter d’encombrer inutilement ce fichier). Ces macros ont été définies dans un fichier nommé « classemereimpl.h » qui contrairement à ce que son extension pourrait laisser supposer n’est pas un fichier d’en-tête voué à être utilisé comme une interface, il s’agit içi d’un fichier d’implémentation au même titre que le fichier « classemere.c ». Pourquoi écrire le corps de certaines méthodes dans des macros fonctions ?  Cette astuce permet de factoriser du code entre les classes mères et les classes filles. En effet,  lorsque nous implémentons les méthodes de la classe filles celles de la classe mère ne peuvent pas être directement réutilisées. Le premier argument des méthodes de la classe mère (argument de type « ClasseMere ») n’est pas le même que le premier argument que les méthodes des classes filles (« ClasseFille »). Cela dit, à par le type du premier argument, rien ne change, en factorisant le corps des méthodes dans des macro-fonctions, on s’affranchit du type du premier argument. Par contre, nous gardons toute la force (ou la faiblesse, à votre convenance…) du typage du langage C, une fois que le préprocesseur aura effectué son travail, le compilateur se chargera d’effectuer toutes les vérifications de type. Néanmoins, cette astuce nous oblige à redéfinir chaque méthode dans le fichier d’implémentation de la classe fille, afin de préciser que le type du premier argument (correspondant à l’instance de la classe sur laquelle s’applique la méthode) à changé.

Lorsque nous avons une instance de la classe « ClasseFille » comment la passer en paramètre à une méthode qui prend en paramètre une instance de la classe « ClasseMere » ? Grace aux deux autres macros-fonctions « ClasseMereUpCast » et  « ClasseMereDownCast » qui permettent respectivement le transtypage de la classe fille vers la classe mère et inversement. Ce transtypage comme vous pourrez le constater à la lecture des sources est artificiel, de plus, il nécessite la présence d’une instance de la classe « ClasseMere ». En effet, la macro-fonction « ClasseMereUpCast » se contente d’affecter à chaque les donnée membre de l’instance de la classe « ClasseMere » la valeur de son équivalent dans la classe fille. La macro fonction « ClasseMereDownCast », fait le même type d’affectations mais dans le sens inverse (de l’instance de la classe mère vers l’instance de la classe fille). Ces transtypages, aussi artificiels soient ils, n’empêcheront pas, à priori, le compilateur de détecter les éventuels problèmes de type.

On peut bien évidement utiliser des macros-fonctions pour factoriser du code dans quasiment toutes les fonctions présentes dans les fichiers d’implémentation (pour alléger le code du constructeur par exemple).

 

Classemere.h :

#ifndef CLASSEMERE_H_INCLUDED

#define CLASSEMERE_H_INCLUDED

 

 

typedef struct ClasseMere *ClasseMere;

typedef struct DescripteurClasseMere DescripteurClasseMere;

struct DescripteurClasseMere

{

 

   void (*supprimer)(ClasseMere);

    double (*methode1)(ClasseMere a, double);

    char*(*methode2)(ClasseMere a,int b, char *c);

 

 

};

 

 

struct ClasseMere

{

   int monChamp1;

   double monChamp2;

   char* monChamp3;

    DescripteurClasseMere* classe;

 

 

};

extern ClasseMere

ClasseMere_creer(int champ1, double champ2, char * champ3);

 

#define ClasseMereUpCast(instance,instanceSousClasse) \

instance->monChamp1=instanceSousClasse->monChamp1; \

instance->monChamp2=instanceSousClasse->monChamp2; \

instance->monChamp3=instanceSousClasse->monChamp3;

 

#define ClasseMereDownCast(instance,instanceSousClasse) \

instanceSousClasse->monChamp1=instance->monChamp1; \

instanceSousClasse->monChamp2=instance->monChamp2; \

instanceSousClasse->monChamp3=instance->monChamp3;

 

#endif // CLASSEMERE_H_INCLUDED

 

 

classemereimpl.h :

 

#ifndef CLASSEMEREIMPL_H_INCLUDED

#define CLASSEMEREIMPL_H_INCLUDED

 

#define ClasseMere_methode1_IMPL(instanceClasseMere, unDouble) \

return instanceClasseMere->monChamp2 + (unDouble);

 

#define ClasseMere_methode2_IMPL( instanceClasseMere, unInt,unCharEtoile) \

if(strlen(unCharEtoile) > (unInt)){\

       return unCharEtoile;\

   }\

    return   instanceClasseMere->monChamp3;

 

#define ClasseMere_supprimer_IMPL(instanceClasseMere) \

 if(instanceClasseMere!=NULL){\

     free(instanceClasseMere);\

 }

 

#endif // CLASSEMEREIMPL_H_INCLUDED

 

classemere.c :

 

#include

#include

#include

#include "classemere.h"

#include "classemereimpl.h"

 

static void ClasseMere_supprimer(ClasseMere maClasse){

ClasseMere_supprimer_IMPL(maClasse);

}

 

static double

ClasseMere_methode1(ClasseMere maClasse,double unDouble){

 

    ClasseMere_methode1_IMPL(maClasse,unDouble);

}

 

static char *

ClasseMere_methode2(ClasseMere maClasse,int unInt, char * unCharEtoile){

ClasseMere_methode2_IMPL(maClasse,unInt,unCharEtoile);

 

 

}

 

static DescripteurClasseMere*

ClasseMere_obtenir_descripteur(){

    static int descripteurDejaInitalise=0; // superieur a 0 si le descripteur est deja initalise

    static DescripteurClasseMere descripteur;

 

    if(descripteurDejaInitalise==0){

        descripteur.supprimer=ClasseMere_supprimer;

        descripteur.methode1=ClasseMere_methode1;

        descripteur.methode2=ClasseMere_methode2;

        descripteurDejaInitalise++;

    }

    return &descripteur;

}

 

 

ClasseMere

ClasseMere_creer(int champ1, double champ2, char * champ3)

{

    ClasseMere monInstance=malloc(sizeof(struct ClasseMere));

 

    monInstance->monChamp1=champ1;

    monInstance->monChamp2=champ2;

    monInstance->monChamp3=champ3;

    monInstance->classe=ClasseMere_obtenir_descripteur();

    return monInstance;

 

}

 

classefille.h:

 

#ifndef CLASSEFILLE_H_INCLUDED

#define CLASSEFILLE_H_INCLUDED

 

 

typedef struct ClasseFille *ClasseFille;

typedef struct DescripteurClasseFille DescripteurClasseFille;

struct DescripteurClasseFille

{

 

   void (*supprimer)(ClasseFille);

    double (*methode1)(ClasseFille a, double);

    char*(*methode2)(ClasseFille a,int b, char *c);

    int (*methode3)(ClasseFille a,int b);

    double (*methode4)(ClasseFille a,int b, double c);

 

};

 

 

struct ClasseFille

{

   int monChamp1;

   double monChamp2;

   char* monChamp3;

   int monChamp4;

   double monChamp5;

    DescripteurClasseFille* classe;

 

 

};

extern ClasseFille

ClasseFille_creer(int champ1, double champ2, char * champ3,int champ4,double champ5);

 

#define ClasseFilleUpCast(instance,instanceSousClasse) \

instance->monChamp1=instanceSousClasse->monChamp1; \

instance->monChamp2=instanceSousClasse->monChamp2; \

instance->monChamp3=instanceSousClasse->monChamp3; \

instance->monChamp4=instanceSousClasse->monChamp4; \

instance->monChamp5=instanceSousClasse->monChamp5;

 

#define ClasseFilleDownCast(instance,instanceSousClasse) \

instanceSousClasse->monChamp1=instance->monChamp1; \

instanceSousClasse->monChamp2=instance->monChamp2; \

instanceSousClasse->monChamp3=instance->monChamp3; \

instanceSousClasse->monChamp4=instance->monChamp4; \

instanceSousClasse->monChamp5=instance->monChamp5;

#endif // CLASSEFILLE_H_INCLUDED

 

classefilleimpl.h :

 

#ifndef CLASSEFILLEIMPL_H_INCLUDED

#define CLASSEFILLEIMPL_H_INCLUDED

#include "classemereimpl.h"

 

#define ClasseFille_methode3_IMPL(instanceClasseFille,b) \

 return a->monChamp4 + (b);

 

 

#define ClasseFille_methode4_IMPL(instanceClasseFille,b,c) \

 return a->monChamp5 + (c) + ((double) b);

 

 

#endif // CLASSEFILLEIMPL_H_INCLUDED

 

classefille.c :

 

#include

#include

#include

#include "classefille.h"

#include "classefilleimpl.h"

 

static void ClasseFille_supprimer(ClasseFille maClasse){

ClasseMere_supprimer_IMPL(maClasse);

}

 

static double

ClasseFille_methode1(ClasseFille maClasse,double unDouble){

 

    ClasseMere_methode1_IMPL(maClasse,unDouble);

}

 

static char *

ClasseFille_methode2(ClasseFille maClasse,int unInt, char * unCharEtoile){

ClasseMere_methode2_IMPL(maClasse,unInt,unCharEtoile);

 

}

 

static int

ClasseFille_methode3(ClasseFille a,int b){

   ClasseFille_methode3_IMPL(a,b);

 

}

 

static double

ClasseFille_methode4(ClasseFille a,int b, double c){

    ClasseFille_methode4_IMPL(a,b,c);

 

}

static DescripteurClasseFille*

ClasseFille_obtenir_descripteur(){

    static int descripteurDejaInitalise=0; // superieur a 0 si le descripteur est deja initalise

    static DescripteurClasseFille descripteur;

 

    if(descripteurDejaInitalise==0){

        descripteur.supprimer=ClasseFille_supprimer;

        descripteur.methode1=ClasseFille_methode1;

        descripteur.methode2=ClasseFille_methode2;

        descripteur.methode3=ClasseFille_methode3;

        descripteur.methode4=ClasseFille_methode4;

        descripteurDejaInitalise++;

    }

    return &descripteur;

}

 

 

ClasseFille

ClasseFille_creer(int champ1, double champ2, char * champ3,int champ4,double champ5)

{

    ClasseFille monInstance=malloc(sizeof(struct ClasseFille));

 

    monInstance->monChamp1=champ1;

    monInstance->monChamp2=champ2;

    monInstance->monChamp3=champ3;

    monInstance->monChamp4=champ4;

    monInstance->monChamp5=champ5;

    monInstance->classe=ClasseFille_obtenir_descripteur();

    return monInstance;

 

}

 

Aucun commentaire: