Introduction au C plus plus
Un article de Games Creators Network.
Attention
Cet article est en attente du choix d'une licence par son auteur. Elle n'est pas soumise à la licence par défaut du GCN.
Cela signifie entre autre chose que si vous n'êtes pas l'auteur de cet article, vous n'avez probablement pas le droit de le modifier.
Le C++ a été développé par Bjarne Stroustrup dans les laboratoires AT&T Bell, dans les années 80. Il représente une évolution du langage C suivant deux axes principaux :
- Création des types de données abstraits ;
- Programmation Orientée Objet (POO).
Pour bien comprendre ce texte, vous devez avoir les notions de variables,types de données (simples ou composées) ,fonctions, structures.
Sommaire |
[modifier] Objets et classes
[modifier] Notion d'objet
Toute donnée d'un type quelconque que l'on construit (déclare) est un Objet. Nous avons donc :
- Les types Simples : int, float, Char, pointeur, ...
- Les objets : Enumération, Tableaux, Structures, Classes.
En simple, un objet est une collection de types simples.
class ObjetGraphique { int x, y; };
La programmation orientée objet a pour vocation de manipuler des objets. Elle définit donc les notions d'encapsulation, héritage, surcharge et virtualité pour les données abstraites. Le terme objet fait souvent référence aux classes qui est le type le plus adapté aux mécanismes cités ci-dessus.
[modifier] Notion de Classe
Une classe définit une structure d'objet. Afin de comprendre facilement cette notion, faisons l'analogie avec les types simples : de la même façon qu'on définit une variable entière en déclarant qu'elle est de type int (int x), on définit un "objet graphique" en déclarant qu'il est issu de la classe ObjetGraphique (ObjetGraphique obj1). On dit que l'objet est une instance de la classe.
Une classe fournit alors un ensemble d'états (les attributs ou données membres) et d'opérations (les méthodes ou fonctions membres) applicables à ses instances, tout comme le sont l'opération et la multiplication pour le type entier.
[modifier] L'encapsulation
L'encapsulation est la possibilité de voir ou non (cela dépend de la portée) les données appartenant à l'objet (comme les variables locales ou globales en C). Ainsi, les données propres à un objet (les données membres) sont privées et ne peuvent être manipulées que par l'objet lui-même. A charge de l'objet de fournir des fonctions de manipulation.
Pour être plus explicite, on utilise les mots réservés private, protected, public qui définissent la visibilité des données :
- Les données (ou fonctions) membres qui sont dans la section public du corps de la classe sont accessibles par tous.
- Les données (ou fonctions) membres qui sont dans la section protected ne sont accessibles que par la classe et ses dérivées (voir héritage).
- Les données (ou fonctions) membres qui sont dans la section private ne sont accessibles que par la classe elle-même.
class ObjetGraphique { private: int x, y; public: void Position(int, int); };
Notez que l'on ne déclare que les prototypes des fonctions, dans le cas contraire la fonction est traitée comme "inline".
Ce mécanisme permet de masquer l'information afin de s'assurer que les données seront manipulées correctement. Ainsi si la représentation des données est changée ou s'il y a une erreur, seul le code des fonctions membres est à éplucher, pas tout le programme.
Pour l'exemple qui suit, x et y ne peuvent pas être négatifs donc toutes les fonctions membres de manipulation doivent vérifier ce point. Ainsi Position(unsigned, unsigned) qui modifiera x et y assurera au programmeur que x et y resteront positifs. De même, x et y ne devraient pas pouvoir dépasser la résolution de l'écran. Toutes ces règles sont implémentées dans les fonctions membres et garantissent une bonne manipulation des données.
Voici donc la classe ObjetGraphique telle que je la concevrais :
class ObjetGraphique { protected : unsigned x, y; public : void Position(unsigned i, unsigned j) { x = i; y = j; } // vu la taille du code nous avons tout intérêt à le traiter en inline. virtual void Affiche() = 0; // cette déclaration interdit l'instanciation de la classe ObjetGraphique // qui devient donc une classe dite "Abstraite" // Affiche doit donc être implémentée par les classes dérivées };
L'implémentation des méthodes de la classe s'exécute après la déclaration de la classe et a la forme suivante :
Type_de_retour nom_de_la_classe::Methode(Parametres)
[modifier] L'héritage
L'héritage est un mécanisme qui permet de dériver les classes afin de profiter de leur implémentation. Ainsi en dérivant la classe ObjetGraphique, la nouvelle classe Point hérite des méthodes de la classe ObjetGraphique :
class Point : public ObjetGraphique { void Affiche(); };
Le mot clé public indique que l'on hérite des données publiques et protégées de la classe. Notez que l'on retrouve la fonction virtuelle Affiche de la classe ObjetGraphique. En effet, le mécanisme de virtualité permet au compilateur de choisir la bonne méthode associée à l'objet lors de sa manipulation. Ainsi la fonction Affiche conserve le même nom quel que soit l'objet dessiné (Point, Triangle...). Le code des fonctions virtuelles peut changer entièrement en fonction des classes dérivées ou être simplement hérité.
Si je déclare maintenant une classe LargePoint héritée de Point, on a :
classe LargePoint : public ObjetGraphique { unsigned Largeur; public: void Largeur(unsigned i) { Largeur = i; } };
Et je peux écrire dans mon programme :
LargePoint test; test.Position(5,5); test.Affiche();
[modifier] Constructeurs et destructeurs
Les constructeurs et destructeurs sont des méthodes spéciales de la classe. Le constructeur porte le même nom que la classe et sert à initialiser la classe. Un constructeur est une méthode qui est automatiquement appelée lors de l'instanciation d'une classe. Il peut être surchargé.
Le destructeur comme le constructeur est appelé automatiquement à la mort de l'objet. Il porte le même nom que la classe, précédé d'un tilde ~.
Le bon exemple d'utilisation des constructeurs/destructeurs est lorsque l'objet utilise l'allocation dynamique de mémoire. D'abord, rappelons brièvement son principe. Les fichiers sources sont compilés pour donner un programme, un exécutable. Cet exécutable comporte deux parties : une partie Instructions et une partie Données Manipulées. Donc si on déclare un int, un unsigned int, un long, etc., le programme réserve de la place dans la mémoire à son chargement. Peu de programmes connaissent la taille des données qu'ils devront manipuler. Il est donc nécessaire d'allouer de la mémoire pendant l'exécution du programme : c'est l'allocation dynamique de mémoire. En C, l'instruction se nommait malloc. En C++ c'est new (la libération de la mémoire est delete). En théorie lorsque un programme se termine, la mémoire qu'il utilisait est libérée. De même que si un programme tente d'écrire dans une page mémoire qui ne lui appartient pas, l'OS le bloque et termine l'application.
En C++, nous placerons donc, quand ce sera nécessaire, le new dans le constructeur, et le delete dans le destructeur. Cela nous assure que l'on libère bien la mémoire utilisée.
Par exemple :
ObjetGraphique(unsigned i,unsigne j) { Position(0,0); } // Constructeur de la classe ObjetGrapique // On remarque qu'un constructeur peut appeler une procédure ~ObjetGraphique() { ... } // Destructeur de la classe. Placez ici les éventuels " delete " // (ou InvalideRect avec windows pour forcer la fenetre à se réafficher)
Note : les destructeurs et constructeurs n'ont pas de type de retour.
[modifier] Surcharge
La surcharge peut s'appliquer aux opérateurs, aux fonctions (membres de classe ou globales) constructeurs compris, mais pas les destructeurs. C'est le fait de nommer deux fonctions avec le meme nom mais de différencier leur liste d'argument. Ainsi le compilateur peut faire la différence lors de l'appel.
Le compilateur C++ lors de la compilation recherche les fonctions sur leur prototype et pas que sur leur nom ainsi on peut déclarer :
void Position(unsigned int, bool, short x = 5); void Position(float, float);
Un exemple serait les fonctions de la bibliothèque math de microsoft qui définit la fonction abs() pour les flottants, les double et les entiers. (en ANSI C il faut différencier abs() de fabs())
[modifier] Polymorphisme
Le polymorphisme est une caractéristique des langages orientés objet qui permet une compatibilité de pointeurs entre les classes qui hérite d'une classe parente commune. Par exemple il est possible de définir une classe abstraite de base (une superclasse) représentant un animal, et d'en dériver grenouille et crocodille. Si on créé un tableau de pointeurs d'animaux, des pointeurs vers grenouille et corcodille pourront être contenus dans ce tableau légalement et sans type-casting.
[modifier] Fonctions amies / Classes amies
Il est également possible de déclarer des fonctions amies aux objets, ce sont des fonctions qui ne sont pas membres mais qui manipulent des données privées. Autrement dit des fonctions globales/statiques qui ont accès aux objets de la classe dans laquelle elle a été délcarée amie.
Pour définir une fonction amie à une classe, il faut la déclarer entièrement dans la classe, en la faisant précéder du mot-clé friend. Voici un exemple de déclaration de fonction amie :
friend ostream& operator << (ostream& ,Matrice& ); // Les classes ou fonctions ou amies se déclarent dans le corp de la classe // ici on surcharge l'operateur de sortie " écran " ce qui permettra d'écrire dans le // programme main "<< ObjetGraphique1" afin de forcer l'affichage. (! Borland C++)
En general implémenter une fonction friend démontre un manque de reflexion sur l'objet, sauf dans le cas de surcharge d'opérateur (+, -, /, <<, etc).
Une classe 1 peut être déclarée amie d'une autre classe si dans cette autre classe on spécifie friend Classe1. L'autre classe aura alors accès aux membres de la classe 1.
par exemple:
class Classe1 { friend Classe2; }; class Classe2 { type objet; };
les fonctions de Classe1 peuvent lire le membre objet de la classe 2.
[modifier] Conclusion
Pour terminer cette présentation je vous invite a télécharger le programme Matrice qui présente tous les point sus-mentionées, exception faite qu'il n'y a pas de de fonction virtuelles, et de regarder ce que cela donne (Compilé à l'époque en C++ 3.01). Je ne présente pas l'utilisation des templates qui sort légèrement du cadre de cette introduction.
Ce document a été publié sur la version 3 du G.C.N. par lau_de_la.
- Auteur Original : lau_de_la
- Date de publication : 17 mai 2002

