Programmation orientée objets en C ? 2/2

Aller en bas

Programmation orientée objets en C ? 2/2

Message par Admin le Ven 5 Nov - 16:03

2ème partie - L'héritage.




Introduction :

Dans cette deuxième partie, nous allons voir comment dériver une classe à partir d'une classe de base.
Nous allons améliorer la pile de la première partie en lui ajoutant de nouvelles caractéristiques. On ne pourra y ajouter que des valeurs comprises entre un mini et un maxi. Les valeurs de ces limites feront partie de l'objet. On pourra les lire ou les modifier via des méthodes.
Ce chapitre fera intervenir une méthodologie un peu surprenante, mais qui fonctionne. Le transtypage d'un pointeur sur structure en un pointeur sur une structure semblable dont le début en est un clone.
Nous élaborerons aussi un deuxième constructeur qui recevra deux paramètres afin d'initialiser le mini et le maxi dés la construction de l'objet.
Nous continuons bien sûr à respecter les règles établies au premier chapitre. On créera donc un nouveau fichier source avec son fichier entête pour la classe dérivée.

Mise en oeuvre :

La structure devant contenir des membres supplémentaires par rapport à la structure de base, elle devra donc être entièrement redéfinie.

Code:
typedef struct TDPile
        {
                int(*Push)(struct TDPile*, int);
                int(*Pop)(struct TDPile*);
                void(*Clear)(struct TDPile*);
                void(*Free)(struct TDPile*);
                int(*Length)(struct TDPile*);
                void(*View)(struct TDPile*);

                int Nombre;
                struct Titem *Top;

                /* Les nouveaux membres toujours à la fin */

                int(*GetMaxValue)(struct TDPile*);
                void(*SetMaxValue)(struct TDPile*,int);
                int(*GetMinValue)(struct TDPile*);
                void(*SetMinValue)(struct TDPile*,int);

                int MaxValue;
                int MinValue;

        } TDPile ;

L'astuce de cette méthode, c'est que les fonctions de la classe de base pourront accéder à cette nouvelle structure.
Mais ceci impose une nouvelle règle à respecter à la lettre, sinon c'est le plantage : Il faut créer la structure de la classe dérivée à l'identique de la classe de base en respectant l'ordre des membres. Ensuite, les nouveaux membres seront ajoutés à la suite.
Pourquoi cela fonctionne ? Quand une structure est créée en mémoire, ses membres sont positionnés les uns à la suite des autres, dans l'ordre de leur déclaration. Si j'en crée une deuxième dont le début est un clone de la première, en mémoire on ne verra pas la différence entre la première et le début de la deuxième. Ce qui fait que des fonctions qui sont faite pour accéder à la première peuvent très bien accéder à la deuxième sans aucun problème.
Dans l'exemple nous avons rajouté deux entiers afin de conserver les valeurs mini et maxi et les pointeurs sur les nouvelles fonctions (membres). Ces fonctions servant à lire ou modifier les valeurs MaxValue et MinValue. En programmation orientée objets on les appelle des assesseurs (ne pas oublié que l'on s'est fixé comme règle de ne pas accéder directement à un membre d'une structure).
Dans l'exemple la structure aura pour nom TDPile donc les fonctions seront préfixées du préfixe TDPile_.
La structure n'étant pas identique, nous devons réécrire les constructeurs :

Code:
TDPile* New_TDPile()
{
      TDPile *This = malloc(sizeof(TDPile));
      if(!This) return NULL;
      TDPile_Init(This);
      This->Free = (void*)TPile_New_Free;
      This->MinValue = 0;
      This->MaxValue = 100;
      return This;
}

Nous réécrirons aussi la fonction d'initialisation :

Code:
static void TDPile_Init(TDPile *This)
{
      This->Pop = (void*)TPile_Pop;
      This->Clear = (void*)TPile_Clear;
      This->Length = (void*)TPile_Length;
      This->View = (void*)TPile_View;

      This->Push = TDPile_Push;

      This->GetMaxValue = TDPile_GetMaxValue;
      This->SetMaxValue = TDPile_SetMaxValue;
      This->GetMinValue = TDPile_GetMinValue;
      This->SetMinValue = TDPile_SetMinValue;

      This->Nombre = 0;
      This->Top = NULL;
}

Comme vous pouvez le remarquer le principe est le même que pour la classe de base, mise à part l'affectation les pointeurs de fonctions (membres) pour les fonctions qui ne sont pas redéfinis. Ils seront initialisés avec l'adresse de leurs fonctions correspondantes dans la classe de base (je les ai castés en pointeur void pour que le compilateur ne m'envoie pas d'avertissement). Cette astuce permet d'utiliser ces fonctions sans s'en préoccuper. C'est le but de l'héritage.

Les assesseurs des membres MinValue et MaxValue sont fait en suivant les mêmes règle que pour la classe de base :

Code:
int TDPile_GetMinValue(TDPile *This)
{
        return This->MinValue;
}
/******************************************************************************/

void TDPile_SetMinValue(TDPile *This, int Value)
{
        This->MinValue = Value;
}
/******************************************************************************/

int TDPile_GetMaxValue(TDPile *This)
{
        return This->MaxValue;
}
/******************************************************************************/

void TDPile_SetMaxValue(TDPile *This, int Value)
{
        This->MaxValue = Value;
}

Comme on ne doit empiler que les valeurs comprises entre MinValue et MaxValue, on redéfinira la fonction (membre) TDPile_Push où on appellera la fonction (membre) TPile_Push de la classe de base seulement si l'on se trouve entre les bornes MinValue et MaxValue.

Code:
int TDPile_Push(TDPile *This, int Value)
{
        if(Value > This->MaxValue || Value < This->MinValue)
                                  return VALUE_OUT_OF_LIMIT;
        return TPile_Push((TPile*)This, Value);       
}

Afin de créer la pile directement avec des bornes de notre choix nous élaborerons un autre constructeur. Nous devrons lui donner bien sûr un nom différent (nous sommes en C).

Code:
TDPile* New_TDPile_MM(int Min, int Max)
{
      TDPile *This = malloc(sizeof(TDPile));
      if(!This) return NULL;
      TDPile_Init(This);
      This->Free = (void*)TPile_New_Free;
      This->MinValue = Min;
      This->MaxValue = Max;
      return This;
}

Il a la particularité de recevoir 2 paramètres qui permettront de lui passer les valeurs mini et maxi afin d'initialiser MinValue et MaxValue dès la construction de l'objet.
Les destructeurs de fesant rien de plus, on ne les a pas redéfinis.

Nous avions mis dans nos règles d'appeler les fonctions (membres) par l'intermédiaire de pointeurs de fonctions fesant partie de la structure pour avoir une syntaxe proche du C++. Mais ceci a un autre avantage considérable : c'est que par ce fait, elles sont virtuelles. Par conséquence, le polymorphisme est tout à fait possible. Ceci pourra faire l'objet d'un autre chapitre.

Codes sources de l'exemple :

Les fichiers Pile.h et Pile.c sont les mêmes qu' au premier chapitre. DPile.h :
Code:

#ifndef CGI_DTPILE_H
#define CGI_DTPILE_H

#define VALUE_OUT_OF_LIMIT  2

#include "Pile.h"    /* Fichier entête de la classe de base */

#ifdef __cplusplus
  extern "C" {
#endif

/*  Structure représantant l'objet DPile. */
typedef struct TDPile
        {
                int(*Push)(struct TDPile*, int);
                int(*Pop)(struct TDPile*);
                void(*Clear)(struct TDPile*);
                void(*Free)(struct TDPile*);
                int(*Length)(struct TDPile*);
                void(*View)(struct TDPile*);

                int Nombre;
                struct Titem *Top;

                /* Les nouveaux membres toujours à la fin */

                int(*GetMaxValue)(struct TDPile*);
                void(*SetMaxValue)(struct TDPile*,int);
                int(*GetMinValue)(struct TDPile*);
                void(*SetMinValue)(struct TDPile*,int);

                int MaxValue;
                int MinValue;

        } TDPile ;

/* Les constructeurs  */
TDPile TDPile_Create(void);
TDPile* New_TDPile(void);

TDPile TDPile_Create_MM(int, int);
TDPile* New_TDPile_MM(int, int);     

/* Les nouvelles fonctions (membres) */
int TDPile_GetMaxValue(TDPile*);
void TDPile_SetMaxValue(TDPile*, int);
int TDPile_GetMinValue(TDPile*);
void TDPile_SetMinValue(TDPile*, int);

/* Les fonctions redéfinie */
int TDPile_Push(TDPile*, int);

#ifdef __cplusplus
}
#endif

#endif

DPile.c :

Code:
#include
#include

#include "DPile.h"  /* Fichier entête de la classe dérivé */

static void TDPile_Init(TDPile*);

TDPile TDPile_Create()
{
      TDPile This;
      TDPile_Init(&This);
      This.Free = (void*)TPile_Free;
      This.MinValue = 0;
      This.MaxValue = 100;
      return This;
}
/******************************************************************************/

TDPile* New_TDPile()
{
      TDPile *This = malloc(sizeof(TDPile));
      if(!This) return NULL;
      TDPile_Init(This);
      This->Free = (void*)TPile_New_Free;
      This->MinValue = 0;
      This->MaxValue = 100;
      return This;
}
/******************************************************************************/

TDPile TDPile_Create_MM(int Min, int Max)
{
      TDPile This;
      TDPile_Init(&This);
      This.Free = (void*)TPile_Free;
      This.MinValue = Min;
      This.MaxValue = Max;
      return This;
}
/******************************************************************************/

TDPile* New_TDPile_MM(int Min, int Max)
{
      TDPile *This = malloc(sizeof(TDPile));
      if(!This) return NULL;
      TDPile_Init(This);
      This->Free = (void*)TPile_New_Free;
      This->MinValue = Min;
      This->MaxValue = Max;
      return This;
}
/******************************************************************************/

static void TDPile_Init(TDPile *This)
{
      This->Pop = (void*)TPile_Pop;
      This->Clear = (void*)TPile_Clear;
      This->Length = (void*)TPile_Length;
      This->View = (void*)TPile_View;

      This->Push = TDPile_Push;

      This->GetMaxValue = TDPile_GetMaxValue;
      This->SetMaxValue = TDPile_SetMaxValue;
      This->GetMinValue = TDPile_GetMinValue;
      This->SetMinValue = TDPile_SetMinValue;

      This->Nombre = 0;
      This->Top = NULL;
}
/******************************************************************************/

int TDPile_GetMinValue(TDPile *This)
{
        return This->MinValue;
}
/******************************************************************************/

void TDPile_SetMinValue(TDPile *This, int Value)
{
        This->MinValue = Value;
}
/******************************************************************************/

int TDPile_GetMaxValue(TDPile *This)
{
        return This->MaxValue;
}
/******************************************************************************/

void TDPile_SetMaxValue(TDPile *This, int Value)
{
        This->MaxValue = Value;
}
/******************************************************************************/

int TDPile_Push(TDPile *This, int Value)
{
        if(Value > This->MaxValue || Value < This->MinValue)
                                  return VALUE_OUT_OF_LIMIT;
        return TPile_Push((TPile*)This, Value);       
}

On voit dans le code de la nouvelle pile dérivée que l'on a pas à se préoccuper du fonctionnement interne de la pile, mais seulement des fonctionnalités supplémentaires que l'on lui a ajoutées. Ceci est un des principaux avantages de la programmation orienté objet.

Voici un exemple d'utilisation de la pile que nous venons de construire.

main.c :

Code:
#include
#include

#include "DPile.h"

int main()
{
        TDPile *MaPile = New_TDPile_MM(0, 30);

        MaPile->Push(MaPile, 10);
        MaPile->Push(MaPile, 25);
        MaPile->Push(MaPile, 33);
        MaPile->Push(MaPile, 12);   

        printf("Borne maxi : %d\n",MaPile->GetMaxValue(MaPile));
        puts("------");
        puts("Affichage de la pile :");
        MaPile->View(MaPile);
        puts("------");
        printf("Nb d'elements : %d\n",MaPile->Length(MaPile));

        MaPile->Free(MaPile);
        MaPile = NULL;

#ifdef __WIN32__
        system("PAUSE");
#endif
        return 0;
}
avatar
Admin
Admin

Messages : 135
Date d'inscription : 21/10/2010

http://depannage-pc.pro-forum.fr

Revenir en haut Aller en bas

Revenir en haut

- Sujets similaires

 
Permission de ce forum:
Vous ne pouvez pas répondre aux sujets dans ce forum