Cours C++.livre(Classe C++)
46 pages
Français

Cours C++.livre(Classe C++)

-

Le téléchargement nécessite un accès à la bibliothèque YouScribe
Tout savoir sur nos offres
46 pages
Français
Le téléchargement nécessite un accès à la bibliothèque YouScribe
Tout savoir sur nos offres

Description

CHAPITRE 12 ClassesLe langage C++ 167einev Télécommunications mjn 12.1 Utilité de la construction “class”La classe C++ est la construction la plus importante de ce langage, relativement à C. Lesclasses sont typiquement utilisées pour introduire un certain degré d'abstraction, en définissantpar exemple de nouveaux types, ou en ajoutant de nouvelles fonctionnalités à des types exis-tant. La notion de classe peut remplacer la construction C struct, comme dans :typedef struct cp_typ{float x, y;} Point;class Point{float x, y;};Dans les deux cas, il est possible de déclarer une variable de type Point comme suit :Point thisPoint;Mais la classe recèle beaucoup plus de choses que la structure C et une déclaration detype. La classe possède (implicitement ou explicitement) 4 attributs associés :1. Des membres. Le terme de membre indique simplement qu’il s’agit d’entités (code,données) faisant partie intégrante de la classe. Par rapport aux éléments x et y de la structurestruct il n’y a pas de différence fondamentale avec les membres x et y de la classe Point.Le terme de membre indique simplement de manière plus précise l’idée d’appartenance. Cha-que identificateur déclaré dans le cadre d’une classe est un membre de cette classe, ce qui si-gnifie qu’il fait partie intégrante de cette classe.2. Zéro ou plusieurs données membres ( data members). Ces membres constituent la re-présentation interne de la classe. Les données implémentent la classe.2. Zéro ou ...

Informations

Publié par
Nombre de lectures 121
Langue Français

Extrait

CHAPITRE 12Le langag
Classes
e C++
167
einev
Télécommunications
12.1Utilité de la construction “class”
mjn
La classe C++ est la construction la plus importante de ce langage, relativement à C. Les classes sont typiquement utilisées pour introduire un certain degré d'abstraction, en définissant par exemple de nouveaux types, ou en ajoutant de nouvelles fonctionnalités à des types exis-tant. La notion de classe peut remplacer la construction C struct, comme dans :
_ typedef struct cp typ { float x, y; } Point; class Point { float x, y; }; Dans les deux cas, il est possible de déclarer une variable de type Point comme suit :
Point thisPoint; Mais la classe recèle beaucoup plus de choses que la structure C et une déclaration de type. La classe possède (implicitement ou explicitement) 4 attributs associés : 1. Desmembres. Le terme de membre indique simplement qu’il s’agit d’entités (code, données) faisant partie intégrante de la classe. Par rapport aux éléments x et y de la structure structil n’y a pas de différence fondamentale avec lesmembresx et y de la classePoint. Le terme de membre indique simplement de manière plus précise l’idée d’appartenance. Cha-que identificateur déclaré dans le cadre d’une classe est unmembrede cette classe, ce qui si-gnifie qu’il faitpartie intégrante de cette classe. 2. Zéro ou plusieursdonnées membres(data members). Ces membres constituent la re-présentation interne de la classe. Les données implémentent la classe. 2. Zéro ou plusieursfonctions membres (function members). Celles-ci représentent l'ensemble des opérations qui peut être appliqué à un objet de cette classe. Elles représentent l'interface de la classe pour les utilisateurs. On appelle souvent ces fonctions membresmé-thodes(methodsla jungle de la terminologie orientée objets, on parle aussi volontiers). Dans demessages(message). 3. Des niveaux d'accès programmatiques. Les membres d'une classe peuvent être définis commeprivate,protected, oupublic. Ces niveaux contrôlent l'accès aux membres depuis le programme. Typiquement, les données membres sont déclaréesprivate, et les fonctions membrespublic. Cette méthodologie est appelée dissimulation de l'implémentation, ouin-formation hiding. Lorsque toute la représentation interne de la classe est déclaréeprivate, on a réalisé une encapsulation. Nous reviendrons sur les membres de typeprotectedplus
168Le langage C++
einev
tard, dans le cadre de l'héritage.
Télécommunications
mjn
4. Un nom de classe associé (tag name), servant de dénomination de type (type speci-fier) pour la classe définie par l'utilisateur. Un nom de classe défini par l'utilisateur peut ap-paraître partout où un identificateur de type prédéfini est autorisé.
Le langage C++
169
einev
Télécommunications
12.2Un exemple : la classe Point
mjn
Nous allons commencer par un court exemple destiné à illustrer pratiquement l’utilisa-tion d’une classe. Il s’agit ici de définir le typePoint, défini par ses coordonnées cartésiennes ou polaires.
12.2.1Définition et implémentation Une classe ayant une représentation interne de niveauprivate et un ensemble de fonctions membres de niveau public est par définition un type de données abstrait (abstract data type). Ainsi, la classePoint, dont le seul but est de définir le type de donnéesPoint, ou coordonnée dans un plan, pourrait se définir comme suit : /*************************************** File : Point.h Author : /users/pro/mjn (Markus Jaton) Date : 05.07.1995 Location : telec2.einev.ch Mail : jaton@einev.ch ****************************************/ // Inclusion de fichiers librairie standards typedef boolean int; #include <iostream.h> class Point { private : // Ces données sont inaccessibles pour l’application. double x, y; public : Point() ; // constructeur par defaut // Constructeur d’assignation, // ou d’affectation Point(double xx, double yy); // constructeur de copie Point(const Point& coord); // Destructeur (ne fait rien) ~Point() {;} // Coordonnées polaires Point(double module, long angleInMilliDegrees); // Opérateur de test d’égalité boolean operator==(const Point& cx) const; // Opérateur d’assignation void operator=(const Point& cx); // Fonction permettant l’impression // sur un ostream. void print(ostream &os);  };
170
Le langage C++
einev
Télécommunications
mjn
est un type de données abstrait. L'application peut l'utiliser sans connaître aucun des dé-tails d'implémentation. Un passage à un système de coordonnées polaires peut s'effectuer sans que l'application ne soit touchée. Essayons d'analyser d'un peu plus près cette définition de classe très simple, pour parvenir à comprendre les règles de base de définition d'une classe.
La définition commence par le mot réservéprivate, suivi de la déclaration de deux valeurs de type double. Ceci est la représentation interne de la classe. On représente la classe Point par ses coordonnées cartésiennes, représentées par des nombres en virgule flottante de double précision. On pourrait également la représenter par son module et son argument: l'ap-plication n'a en principe pas à s'occuper de ceci. D'ailleurs, toute tentative de la part d'une ap-plication, d'adresser le membre x de la variable z, elle-même de type Point, par exemple, générerait de la part du compilateur l'erreur suivante :
myProg.C, line XXX : error : Cannot access z.x : private member //HPC++ Suit une partie précédée par le mot reservépublic. C'est ici que commence l'interface de la classe pour les utilisateurs. En principe, les utilisateurs normaux de la classe ne voyent que cette partie. Cette partie liste lesopérationsqu’un utilisateur peut demander à un objet de la classePointOO, on dira que cette partie spécifie les messages auxquels. En jargon peut répondre un objet de typePoint. La déclaration que nous avons donnée çi-dessus représente l’interfacede la classe. Cet interface est (du point de vue de sa signification) équivalent à un module de définition en MO-DULA-2, ou à une spécification en ADA. Cet interface est habituellement stocké dans un fi-chier séparé, unheader file(fichier en-tête). Le fichier en-tête ne contient pas de code1, mais uniquement des définitions, ainsi que le veut le bon usage en C et en C++. Le code est contenu dans un fichier séparé, lefichier implémentation(implementation file). Cette convention correspond exactement à la convention en langage C traditionnel, pour définir les librairies externes. La définition de classes au moyen de C++ par l’intermédiaire de fichiers en-tête est néanmoins plus puissante que l’utilisation habituelle de fichiers en-tête en C, parceque les concepts utilisés par le langage permettent une plus grande abstraction que la simple définition de prototypes de fonction. Concentrons-nous maintenant sur l’implémentation de cette classe Point. Nous allons tout d’abord la donner telle quelle, sans explications préalables de la syntaxe, et nous revien-drons ensuite progressivement sur les divers éléments de cette implémentation.
/*************************************** File : Point.C Author : /users/pro/mjn (Markus Jaton) Date : 05.07.1995 Location : telec2.einev.ch
1. En réalité, le fichier en-tête peut contenir du code, comme nous le verrons par la suite. Ce code est alors appelé«en-ligne implicite»(implicit inline code). Il est fortement déconseillé de publier le code de cette façon, sauf pour certaines fonctions particulièrement triviales. Il est préférable de dissimuler toujours l’implémentation, au besoin en la déclarant comme «inline» de manière explicite dans le fichier implémentation.
Le langage C++
171
einev
Télécommunications
Mail : jaton@einev.ch ****************************************/ #include <math.h> #include «Point.h» const double pi = 3.1415926; const double RadToDeg = 360 / 2 * pi; Point::Point() { // Constructeur par défaut x = y = 0; } Point::Point(double xx, double yy) { // Constructeur normal x xx; y = yy; = } Point::Point(const Point& coord) { // Constructeur de copie x = coord.x; y = coord.y; } Point::Point(double module, long angleInMilliDegrees) { // Constructeur par coordonnées polaires x = module * cos(angleInMilliDegrees * RadToDeg); y = module * sin(angleInMilliDegrees * RadToDeg);   } boolean Point::operator==(const Point& cx) { // Test d’égalité des deux coordonnées : return ((x == cx.x) && (y == cx.y)); } Point& Point::operator=(const Point& cx) { x = cx.x; y = cx.y; return *this; } void Point::print(ostream &os) { // Fonction permettant l’impression sur un ostream. os<<x<<,”<<y<<endl; } 172
Le langage C++
// Opérateur d’assignation
mjn
einev
Télécommunications
mjn
Notons la syntaxe particulière utilisée pour dénoter un membre : la méthode print de-vient, dans le fichier implémentationvoid Point::print(). On exprime par là qu’il s’agit d’une fonction -d’une méthode- propre à Point, donc appartenant au contexte de Point. On pourrait, dans le même fichier, définir une autre fonction print() dans un contexte différent sans qu’il n’y ait conflit.
12.2.2Instanciation d’un objet de la classe Point
Cette définition et l’implémentation ayant été dûment enregistrée, comment peut-on utiliser la classe et le code associés? La classe Point n’est en fait pas grand’chose d’autre qu’une déclaration de type. Pour instancier une variable d’un type donné, en C comme en C++, il suffit d’écrire _ <TYPE DONNE> uneVariable; Ainsi, pour une variable de type int, on écrira :
int uneVariableDeTypeInt; De manière semblable, on peut définir une variable de type Point par : Point uneVariableDeTypePoint; En C++, on utilisera volontiers les expressions orientées objets; ainsi on dira de la ligne çi-dessus qu’il s’agit de l’noatcnaiitsnid’unobjetde typePoint.uneVariableDeTy-pePointest ainsi une instance de la classePoint.
12.2.3Membres privés
La classe Point définit deux membres privés : x et y. En principe, l’application n’a pas à connaître l’existence de ces deux membres, puisqu’elle n’y a pas accès. Alors, pourquoi pu-blier leur existence dans le fichier Point.h ? C’est une question justifiée; du point de vue théo-rique, il est effectivement peu judicieux de faire connaître l’existence de x et de y à une application qui ne peut de toutes façons pas y accéder. D’un point de vue pratique, on est ici limité par le choix qui a été fait - pour des raisons de compatibilité avec C - d’utiliser le mé-canisme d’inclusion de fichiers (#include) du préprocesseur pour définir une classe. Pour calculer la taille de la classe en mémoire, le compilateur a besoin de la définitioncomplète (membres privés inclus) de la classe. Soit on lui fournit cette définition au moyen de fichiers de spécification précompilés (qui ne peuvent alors plus être importés simplement par #inclu-de !), soit on définit la classe au niveau source, ce qui permettra au compilateur de la traiter comme unestructnormale. C++ a choisi la deuxième possibilité, évitant ainsi la définition de mots réservés supplémentaires (du styleimport), au détriment d’une plus grande consis-tance du langage. Notons que d’une manière tout à fait générale, les membres privés peuvent être consti-tués de code, de données, ou d’un mélange arbitraire des deux.
Le langage C++  
173
einev
Télécommunications
mjn
Les membres de la classe ont quant à eux un accès tout à fait libre aux membres privés.
12.2.4Membres publics
-Dans la classe Point, tous les autres membres sont déclarés de typepublic. Ceci si gnifie qu’ils sont tous accessibles depuis l’extérieur de la classe, donc qu’ils sont accessibles depuis une application, ou depuis une autre classe, par exemple. Ainsi, pour accéder au mem-bre print() d’une instance de type Point, on pourrait utiliser le segment de code suivant : int main(int, char**) { Point cc; cc.print(); // Appel du membre public print() de l’instance cc } Il n’y a pas de différence syntactique entre l’accès à un membre public d’une classe, et l’accès à un élément d’unestruct.
12.2.5rustonCtcuesr
La classe Point définit trois constructeurs. Un constructeur sert à initialiser correctement la classe, ou plus exactement touteinstancede la classe. Un constructeur permet de garantir, par l’utilisation de code judicieux, l’état initial d’une instance de la classe considérée. C++ permet de définir un nombre arbitraire de constructeurs pour une classe donnée, pour autant que leur signature diffère. Ainsi, compte tenu de la définition et de l’implémentation de la classe Point çi-dessus, un programme pourrait effectuer les déclarations suivantes : int main(int, char**) { Point cx; // Appel du constructeur par défaut Point cy(1.0, 4.23); // Constructeur normal // (initialisation) Point cz(cy); // Appel du constructeur par copie. } L’appel du constructeur par défaut (Point cx;) aura pour effet la génération d’une ins-tance de la classe Point appelée cx, dont les valeurs des membres privés x et y valent 0. Ces valeurs sont assignées par le constructeur par défaut. L’appel du constructeur “normal” (Point cy(1.0, 4.23);parfois appelé constructeur d’assignation par certains auteurs, appelation tout aussi abusive que le “normal” utilisé ici) a pour effet d’imposercy.x = 1.0etcy.y = 4.23. L’appel du constructeur de copie (Point cz(cy);) a pour effet de faire de cz une copie de cy. Ainsi, on auracz.x = 1.0etcz.y = 4.23. Le constructeur de coordonnées polaires permet de définir une coordonnée à l’aide de son module et de son argument. L’implémentation montre que le constructeur va convertir les
174
Le langage C ++
einev
Télécommunications
mjn
paramètres passés en coordonnées cartésiennes, de manière à garantir l’uniformité du com-portement de la classe, quelle que soit la manière dont l’instanciation est faite (ou, si vous pré-férez, le constructeur utilisé pour générer une instance de la classe Point).
12.2.6eDurtsuetcr La classe Point définit un destructeur qui ne fait rien de particulier (destructeur compo-d’une instruction vide). D’une manière plus générale, un destructeur fait pendant au cons-tructeur: un destructeur est censé défaire tout ce que le constructeur -voire d’autres méthodes de la classe, au cours de la durée de vie d’une instance de cete classe- a fait. Il doit permettre, lors de la destruction d’une instance de la classe Point, de s’assurer que toutes les ressources que l’instance considérée aurait pu occuper au cours de sa durée de vie seront libérées. Dans notre cas, aucune ressource particulière n’est occupée, et nous pouvons de ce fait nous con-tenter d’une implémentation minimaliste de destructeur.
12.2.7Méthsedo
Les méthodes de la classe Point sont portion congrue, puisqu’elles se réduisent au nom-bre de une (sans compter les méthodes particulières que sont les opérateurs). Seule la méthode print() a été définie ici, qui imprime la valeur des deux membres privés. Notons que cette mé-thode peut accéder aux membres privés, mais uniquement parcequ’elle est un membre de la classe. Ainsi, le code suivant : Point cx(1.0, 2.3); cx.print(); ... a pour effet : 1.0, 2.3 Notons que la méthode print() ne prend aucun paramètre. Beaucoup d’adeptes de la programmation en PASCAL (ou autre langage structuré traditionnel) s’en étonnent: ils vou-draient pouvoir écrire quelque chose commeprint(cx). Ce serait effectivement indispen-sable sicx n’était pas une instance de Point. cx étant une instance,c’est la méthode print() de cx qui va être appelée. print() appartient à l’objet cx au même titre que les membres pri-vés x ou y; l’indication d’appartenance à cx est implicitement contenu dans la notation cx.print().
12.2.8srérOpeuat
Notre classe Point définit de plus deux opérateurs: le test d’égalité et l’assignation.Cu-rieusement, semble-t-il, ces deux opérateurs ne prennent qu’un seul argument, bien qu’en toute logique, l’opérateur ==, par exemple, doive s’effectuer sur deux opérandes ! Il n’y a pourtant pas d’erreur, et la raison de cet apparent manque d’opérande est similaire à l’appa-rent manque de paramètre discuté au paragraphe12.2.7, page175. La définition de l’opérateur d’assignation (=) est la suivante :
Le langage C++
175
einev
Télécommunications
Point& Point::operator=(const Point& cx);
mjn
L’opérande donné comme paramètre est toujoursl’opérande de droitedans le cas d’un opérateur membre binaire. L’opérande de gauche est -implicitement- l’instance de la classe à laquelle l’opérateur est appliqué. Ainsi, dans le segment de code suivant : int main(int, char**) { Point cx(2.4, 3.1); Point cy; cy = cx; } L’expressioncy = cxsignifie “appliquer l’opérateur = de l’instance cy avec pour pa-ramètre cx”. L’opérande de gauche est donc cy, l’opérande de droite cx. On a bien deux opé-randes comme habituellement, mais le fait que l’opérateur soit membre d’une classe implique que le membre de gauche est passé implicitement. Une autre manière, parfaitement correcte, d’invoquer l’opérateur “=” de cy serait cy.operator=(cx); Il en va de même pour les opérateurs unaires, sauf que le seul opérande qu’ils traitent est passé par défaut.
176
Le langage C++
einev
Télécommunications
12.3Constructeurs et destructeurs
mjn
Lors de l’examen de notre petite classe Point, nous avons abordé les constructeurs (qui servent à créer une instance d’une classe) et le destructeur servant à détruire une instance. Bien que l’on puisse définir plusieurs constructeurs, on ne peut fort logiquement pas appeler explicitement un constructeur pour un membre déjà crée (se qui équivaudrait à construire quelque chose de déjà construit!). Par contre, on ne peut définir qu’un seul destructeur.
12.3.1Initialisation par le constructeur Lors de l'instanciation d'une classe définie par l'utilisateur, le compilateur effectue auto-matiquement la réservation de mémoire nécessaire aux membres de la classe considérée. Cet-te réservation se fera au moyen des fonctions standard malloc, calloc, etc... Lorsque cette place mémoire est réservée, cette zone mémoire devra être initialisée. C++ permet à l'utilisa-teur de définir pour ce faire un constructeur, qui est appelé lors de l'instanciation, après que l'espace pour les membres ait été reservé avec succès. L'initialisation effective des valeurs dans la zone mémoire réservée se fait soit au moyen d'instructions explicites d'assignation aux membres privés, soit au moyen de laliste d'initialisationque l'on peut fournir avec le constructeur, soit encore au moyen d'un mélange des deux techniques : class A { private : int x, y; public : A(int b1, int b2) : x(b1), y(b2) {} // Utilisation de la liste d'initialisation }; class A { private : int x, y; public : A(int b1, int b2) { x = b1; y = b2; } // Assignation explicite }; class A { private : int x, y; public : A(int b1, int b2) : x(b1) { y = b2; } // Initialisation + Assignation explicite }; Quelle différence entre les deux techniques ? Pratiquement, et pour les exemples très simples qui nous préoccupent maintenant, aucune. En fait, la différence fondamentale réside dans l’ordre d’exécution : la liste d’initialisation est exécutéeavantl’instanciantion d’une ob-
Le langage C++
177
  • Univers Univers
  • Ebooks Ebooks
  • Livres audio Livres audio
  • Presse Presse
  • Podcasts Podcasts
  • BD BD
  • Documents Documents