See site in english Voir le site en francais
Website skin:
home  download  forum  link  contact

Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

Author Topic: [C++] La programation orientée objets  (Read 1732 times)

0 Members and 1 Guest are viewing this topic.

Offline DanSteph

  • Administrator
  • Legend
  • *****
  • Posts: 15407
  • Karma: 256
  • Hein, quoi !?
    • FsPassengers
07 August 2007, 03:17:44
Ca fait peur ce truc la mais en fait c'est tout con... Quand vous programmer pour Orbiter vous en faite mais vous ne le
savez pas. Je vais expliquer ici ce qu'est la programation orientée objet (OO) et pourquoi c'est utile.
Suivez bien et ne passez pas a l'étape suivante avant d'avoir bien compris.

Soit un chat qui est un "objet" comme un autre, il s'appelle mickey:



On va faire un programme qui va garder sont age, sont degre de faim et de soif
et des fonctions pour lui donner à manger à boire.

Je vous montre d'abord ce que ca donne en programation classique, puis en augmentant les besoins
pourquoi ca devient impossible à gerer et comment le OO vient à notre secours.

D'abord en programation classique, notre programme aura besoin de trois variables et de deux fonctions:

Code: [Select]
///////////////////////////////////////////////////////
// Programme "ChatMickey"
///////////////////////////////////////////////////////
int iAgeChat=5;     // 5 ans
int iFaimChat=10;  // 10% pas très faim
int iSoifChat=20;   // 20% moyennement faim
// nos fonctions
void FaitBoireLeChat();
void FaitMangerLeChat();

Et dans nos fonctions on pourrait faire ca par exemple:

Code: [Select]
void FaitBoireLeChat()
{
      iSoifChat=0;  // remet a 0% la soif

}


Tout ca c'est très bien mais maintenant on veux un élevage de chat, il va falloir multiplier nos variables, on pourrait
faire plusieurs variable avec des noms différent par chat:

Code: [Select]
int iAgeChatMickey
int iAgeChatFelix

Mais ce serait completement idiot car il faudrais maintenant rajouter une fonction par chat:

Code: [Select]
void FaitBoireLeChatMickey()
{
      iSoifChatMickey=0;  // remet a 0% la soif

}
void FaitBoireLeChatFelix()
{
      iSoifChatFelix=0;  // remet a 0% la soif

}


Donc le plus simple c'est de faire des tableau de variable et un selecteur:

Code: [Select]
int QuelChatOnTraite=0;
int iAgeChat[10]={0];

Et après on donne l'age au premier chat et au deuxieme et au troisieme etc etc:

Code: [Select]
QuelChatOnTraite=0;
iAgeChat[QuelChatOnTraite]=5;
QuelChatOnTraite=1;
iAgeChat[QuelChatOnTraite]=2;
QuelChatOnTraite=2;
iAgeChat[QuelChatOnTraite]=4;

Et dans la fonction si on veut donner à boire au 5eme chat on ferrait comme cela:

Code: [Select]
QuelChatOnTraite=5;
FaitBoireLeChat();

// fonction fait boire le chat
void FaitBoireLeChat()
{
      iSoifChat[QuelChatOnTraite]=0;  // remet a 0% la soif

}


Conclusion de la programation classique:

La le programmme est simple, on à 5 chat, et si on en veut 10, 1000 ? 10000 avec des fonctions avancées et des dizaines de variables ?
Comme vous programmez pratiquement explicitement pour chaque chat que vous avez, vos variables sont multipliées a l'infini, le programme va devenir extrement compliqué et vous n'avez rien vu encore...

Le chat No 2 est mort... on ne le veut plus du tout on fait quoi ? on rajoute une variable il est mort ? et on
garde ses variables inutiles ?  et si la moitié de l'elevage creve et qu'on en reprend 1000 et qu'on avait prévu que 1000 place ? On rajoute 1000 variables ? et si on garde l'elevage 50 ans car c'est un simu de chat ???

Pour faire de la place on peut commencer à copier des variables chats sur les tableaux par exemple, vu que le 2 est mort on copie les variables du 3 sur le 2, du 4 sur le 3, pour "combler le vide" du chat 2.... et le chat 3 est devenu le 2 et

AAARGHHHHHHHHHHHHHHHH NOOOOOOOOOOOOOOOON :wall:

La c'est le début de la fin !!!! Ca devient compliqué, ingérable, comme c'est compliqué c'est forcement pleins de bug, on y passe un temp fou.... Stop !!!!


La programation orientée Objet:

En programation classique on s'occupe explicitement de chaque chat, chaque "chat" (objet") à ses propre variables, ca devient ingérable quand le nombre d'objet augmente.

Avec la programation objet on ne vas s'occuper que d'UN objet, l'objet "CHAT", on va ecrire tout notre programme en fonction d'un seul chat et après il suffira de dupliquer l'objet.

pour cela nous allons définir une classe "chat" elle contiendra comme avant nos variables et nos fonctions:

Code: [Select]
///////////////////////////////////////////////////////
// Classe "Chat"
///////////////////////////////////////////////////////
class Chat
{
     int iAgeChat;
     int iFaimChat;
     int iSoifChat;
     // nos fonctions
     void FaitBoireLeChat();
     void FaitMangerLeChat();
}

Un classe contient pratiquement toujour un constructeur et un destructeur qui serons utilisé quand on crera un nouvel objet ou le detruira, le constructeur est la bonne place pour initialiser les variables et proceder à toutes les initialisations et le destructeur la bonne place pour liberer les ressources allouée
(si il y en a)
Le constructeur est une fonction comme une autre sauf qu'elle a le nom de la class mais on peut lui donner des variables. Le destructeur lui est toujours précédé du signe ~

Notre classe avec constructeur et destructeur.

Code: [Select]
///////////////////////////////////////////////////////
// Classe "Chat"
///////////////////////////////////////////////////////
class Chat
{
     Chat(int Age,int Faim,in Soif);     // constucteur
     ~Chat();                             // destructeur
     int iAgeChat;
     int iFaimChat;
     int iSoifChat;
     // nos fonctions
     void FaitBoireLeChat();
     void FaitMangerLeChat();
}


Nos fonction serons maintenant définie comme faisant partie de la classe chat avec les "::"

Code: [Select]
// constructeur qui initialise nos variables de classe:
void chat::chat(int Age,int Faim,in Soif)
{
     iAgeChat=Age;
     iFaimChat=Faim;
     iSoifChat=Soif;
}

// fonction FaitMangerLeChat
void chat::FaitMangerLeChat()
{
     iFaimChat=0;   // remise a 0% de la faim
}


L'avantage de cela ? c'est que maintenant vous n'aurez plus qu'une seul variable à garder par chat, la variable "poignée" ou "handle" de ce chat, de cet "objet". Dans le constructeur on a mis quelques variable, ca nous permet d'initialiser un chat en une ligne.

Voici notre variable, notre "poignée", notre "handle" de la classe, comme le "*" l'indique c'est un pointeur:

Code: [Select]
Chat* Mickey=0;   // handle de classe "Chat"
Et voila comment on crée un chat en une ligne:

Code: [Select]
Mickey= new Chat(5,10,20);     // age , faim, soif
L'operateur "new" "instancie" votre classe, a ce moment, le C++ reserve une zone mémoire, duplique votre classe dedans avec toutes ses variables et vous retourne dans votre "handle" l'adresse de cette zone mémoire. Pour appeler une fonction vous utiliserez la poignée avec le signe "->"

Code: [Select]
// fait boire mickey
Mickey->FaitBoireLeChat();

On voit tout de suite l'avantage, en dehors de votre classe vous n'avez plus qu'une variable "poignée" à gerer par objet:

Code: [Select]
Mickey->FaitBoireLeChat();
Donald->FaitBoireLeChat();
Felix->FaitBoireLeChat();
Arthur->FaitBoireLeChat();

Arthur est mort ? facile: (dans ce cas le destructeur est appellé automatiquement)

Code: [Select]
delete(Arthur);
Quel est l'age de donald ? facile aussi:

Code: [Select]
QuelAge=Donald->iAgeChat;
Maintenant on peut aussi faire des fonctions plus globales pour manipuler les objets de nos classes il suffit de passer le handle:

Code: [Select]
// le chat a faim et soif d'un coup
void IlAFaimEtSoif(Chat * HandleChat)
{
     HandleChat->iFaimChat=100;   // faim a 100%
     HandleChat->iSoifChat=100;   // soif a 100%
}

Et on appelerais cette fonction en lui passant le handle du chat voulu:

Code: [Select]
   IlAFaimEtSoif(Mickey)
}




Conclusion:

Vous n'avez programmé que pour un seul chat, vous n'avez défini qu'une fois des variables et des fonctions et maintenant vous pouvez declarer autant d'objets que vous voulez, 10, 20 ou 10000 cela reviendra au même. Vous ne codez que pour un objet !

L'autre énorme avantage de la programation Objet c'est que vous definissez une interface pour bosser avec des objets, vous ne vous embeter qu'une fois à faire le détails (dans la classe) et après vous pouvez manipuler votre objet avec l'interface. Imaginez la classe comme le tableau de bord d'une voiture, volant, levier de vitesse, clignotant, très facile à manipuler. Le code à l'intérieur de votre classe s'occupera du détail, injection, relais du clignotant, fils, combustion. Les fonctions serons le tableaux de bord.

Transposez ca maintenant à Orbiter, jetter un oeil au code source du deltaGlider et imaginez si il devait garder toutes ses variables dans des tableaux en prévision d'avoir plusieurs deltaglider dans un scenario... Imaginez un ShuttlePB, un NASSP un deltaGlider tous avec des variables de tableaux, tous globale, vous avez choisi "altitude" comme nom de variable ? pas de bol, NASSP aussi...
Impossible à faire, en tout cas idiot, une perte de temps collossale et un véritable appel pour avoirs de nombreux bug.

Regardez maintenant toujours dans le source du deltaglider dans ovcinit() et ovcexit, vous verrez l'endroit ou Orbiter instancie et détruit la classe deltaglider c'est à dire, crée ou detruit un vaisseau d'un scenario. Comme vous pouvez le voir Orbiter récupere le handle de votre classe avec le return, c'est avec ce handle qu'il va appeller vos différentes fonction callback.

Code: [Select]
// --------------------------------------------------------------
// Vessel initialisation (instanciation et construction de l'objet DeltaGlider)
// --------------------------------------------------------------
DLLCLBK VESSEL *ovcInit (OBJHANDLE hvessel, int flightmodel)
{
return new DeltaGlider (hvessel, flightmodel);
}

// --------------------------------------------------------------
// Vessel cleanup (Destruction)
// --------------------------------------------------------------
DLLCLBK void ovcExit (VESSEL *vessel)
{
if (vessel) delete (DeltaGlider*)vessel;
}

Vous ? vous n'avez programmé que pour un seul objet, l'objet "deltaglider"

Elle est pas belle la vie en "OO" ? :badsmile:

Ceci n'est qu'un survol évidement, c'est les grandes lignes,le principe, le C++ "OO" permet bien plus de chose et les classes sont d'une souplesse incroyable, on peut faire encore bien des choses mais pour vous expliquer tout ca il faudrais un bouquin... ;)



Message modifié ( 07-08-2007 14:40 )


Offline DanSteph

  • Administrator
  • Legend
  • *****
  • Posts: 15407
  • Karma: 256
  • Hein, quoi !?
    • FsPassengers
Reply #1 - 07 August 2007, 04:51:41
Un note sur les handles de la programation OO

Les handles invalide sont une des première cause de CTD (crash de l'application)

Tapez ca sans instancier votre classe:

Code: [Select]
Mickey->FaitBoireLeChat();
Comme le pointeur mémoire, la poignée ne pointe sur rien vous ordonnez a l'ordinateur d'executer une fonction a une
adresse qui ne contient rien = CTD.

Par convention TOUT les handles invalide DOIVENT être a zéro et on teste presque TOUJOURS un handle avant de
l'utiliser.

Quand on declare un handle on l'initialise tout de suite à zéro, il ne pointe sur rien:

Code: [Select]
Chat* Mickey=0;
Après instanciation le handle à une valeur différente de zéro, il est valide:

Code: [Select]
Mickey= new Chat(5,10,20);     // age , faim, soif
pour tester un handle c'est dès l'ors simple:

Code: [Select]
if(Mickey==0)
{
    // handle invalide vaut mieux eviter d'y toucher
}

Dès lor notre fonction globale DOIT contenir une protection contre un mauvais handle:

Code: [Select]
// le chat a faim et soif d'un coup
void IlAFaimEtSoif(Chat * HandleChat)
{
     if(HandleChat==NULL)
          return;                                   // protection, si le handle est null retourn sans y toucher
     HandleChat->iFaimChat=100;   // faim a 100%
     HandleChat->iSoifChat=100;   // soif a 100%
}


Et quand on delete un objet on remet TOUJOURS le handle a null après (le delete ne le fait pas)

Code: [Select]
delete(Mickey)
Mickey=0;


Offline Papyref

  • Legend
  • ******
  • Posts: 5348
  • Country: France fr
  • Karma: 346
  • Je suis dans la Lune ne pas me déranger
Reply #2 - 07 August 2007, 09:42:13
Merci Dan !
Encore une fois c'est passionnant. Tu devrais écrire un livre, ça serait surement mieux que ce que l'on trouve en
librairie en général touffu et mal bâti

:sage: Papyref

« Last Edit: 07 August 2007, 09:42:13 by Papyref »