Developpez.com

Club des développeurs et IT pro
Plus de 4 millions de visiteurs uniques par mois

Programmation orientée objet en C

2e partie - L'héritage

L'auteur

Site personnel

I. 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.

II. Mise en œuvre

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

 
Sélectionnez
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 faites 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 objet on les appelle des assesseurs (ne pas oublier 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 :

 
Sélectionnez
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 :

 
Sélectionnez
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, mis à part l'affectation les pointeurs de fonctions (membres) pour les fonctions qui ne sont pas redéfinies. Ils seront initialisés avec l'adresse de leur fonction correspondante 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 faits en suivant les mêmes règles que pour la classe de base :

 
Sélectionnez
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.

 
Sélectionnez
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).

 
Sélectionnez
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 deux 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 ne faisant 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 fonction faisant 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équent, le polymorphisme est tout à fait possible. Ceci pourra faire l'objet d'un autre chapitre.

III. Codes source de l'exemple

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

 
Sélectionnez
#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 :

 
Sélectionnez
#include<stdlib.h>
#include<stdio.h>
 
#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ée objet.

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

main.c :

 
Sélectionnez
#include <stdlib.h>
#include <stdio.h>
 
#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;
}



Bonne lecture,
CGi.

C/C++
  Les pointeurs du C/C++.   Les listes chaînées.             Liste simple.             Liste triée.             Liste double.   Les arbres.   Les tas.   Le C orienté objets ?
  1 - La fenêtre principale.   2 - Contrôles et messages.   3 - Les commandes.   4 - Dialogue std.   5 - Contexte de périph.   6 - Dessiner.   7 - Les ressources.   8 - Dialogue perso.   9 - Dialogue comm.   10 - Les accélérateurs.
C++ BUILDER
  Trucs et astuces.   Composant.   TRichEdit.   TDrawGrid.   Application MDI.   TThread.   wxWidgets.   Style Win XP.
  Première application.   Construire un menu.   Dessiner.   Sisers, Timers...   Dialogues standards.   Dialogues perso.
DotNet
  Composant C# Builder.   Contrôle WinForm.   Application MDI.
  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 CGI. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.