le langage c++ - jmvanel.free.frjmvanel.free.fr/c++/langc++ d madec.pdf · xle langage c++ a une...

311
LE LANGAGE C++ Rév B auteur: Denis MADEC [email protected]

Upload: dinhnguyet

Post on 16-Sep-2018

216 views

Category:

Documents


0 download

TRANSCRIPT

1

LE LANGAGE C++

Rév B

auteur: Denis [email protected]

2

2

PLAN DU COURSINTRODUCTIONL'APPROCHE OBJETLE LANGAGE C++

– PRESENTATION DE C++– CLASSES ET OBJETS– OBJETS, TABLEAUX ET POINTEURS– ARGUMENTS PAR DEFAUT– MOT-CLE THIS – FONCTIONS ET METHODES INLINE– VARIABLES ET METHODES DE CLASSE– LA NOTION DE REFERENCE– LE PASSAGE D'ARGUMENTS– LE RETOUR D'OBJETS– LES CONSTANTES– ALLOCATION DYNAMIQUE DE MEMOIRE– LE CONSTRUCTEUR DE COPIE

Toute représentation ou reproduction intégrale ou partielle faite sans le consentement de l'auteur Denis MADEC ou de ses ayants droits ou ayant cause est illicite selon le code de la propriété intellectuelle du 1er juillet 1992 et constitue une contefaçon réprimée par le code pénal.

3

3

PLAN DU COURSLE LANGAGE C++ (suite)

– LA SURCHARGE DE L'OPERATEUR=– FONCTIONS ET CLASSES AMIES– SURCHARGE D'OPERATEURS– LES CLASSES INTERNES– LA COMPOSITION– L'HERITAGE– LE POLYMORPHISME– IDENTIFICATION DE TYPE A L'EXECUTION (RTTI)– LES EXCEPTIONS– LA GENERICITE

LA BIBIOTHEQUE STLBIBLIOGRAPHIE

4

4

INTRODUCTION

le langage C++ a une vingtaine d'années et son avenir, à long terme sans doute compromis, reste dans l'immédiat plutôt radieuxc'est encore aujourd'hui le langage objet le plus utilisé dans le monde et ses domaines d'utilisation sont extrêmement diversifiésson apprentissage, réservé aux connaisseurs du langage C qu'il englobe, reste long et difficilece support n'a pas pour ambition de couvrir toutes les subtilités du langage C++, mais d'aller à l'essentiel, de façon à permettre rapidement la conception et la réalisation de programmes objets en C++ce document n'est qu'un support de cours, dont le rôle est d'appuyer une formation avec exercices dispensée par un formateur

5

L'APPROCHE OBJET

LA PROGRAMMATION CLASSIQUELA PROGRAMMATION OBJET

6

6

LA PROGRAMMATION CLASSIQUE

La programmation classique s'attache à découper le problème àrésoudre en entités fonctionnellesles entités fonctionnelles se préocupent peu des données sur lesquelles elles agissentdans le meilleur des cas, les données sont protégées au sein d'une entité fonctionnelle, car connues d'elle seuledans d'autres cas, les données sont partagées par plusieurs entités fonctionnelles

7

7

LA PROGRAMMATION OBJET

les concepteurs des langages objet se sont attachés à trouver des solutions pour résoudre les problèmes rencontrés en programmation classique:– faible protection des données d'ou un grand nombre de bogues potentiels– faible réutilisabilité des développements effectués– difficulté à modifier des logiciels existants sans compromettre leur fiablité– maintenance rendue difficile par la dépendance des entités fonctionnelles

entre elles

8

LA PROGRAMMATION OBJET

LES PRINCIPESLES FACTEURS DE QUALITE D'UN LOGICIELLA TERMINOLOGIELES PROPRIETES DE L'APPROCHE OBJETLES PRINCIPAUX LANGAGES ORIENTES OBJET

9

9

LES PRINCIPES

Masquage de l'information– l'information n'est disponible que si l'on en a un réel besoin

Réutilisabilité– évite la ré-écriture de code

Extensibilité– permet d'étendre les fonctionalités sans impact majeur sur les développements

déjà effectués

10

10

LES FACTEURS DE QUALITE D'UN LOGICIEL

Exactitude– pour des données valides en entrée, les sorties sont exactes

Robustesse– pour des données erronées en entrée, les sorties sont "raisonnables"

Stabilité– le logiciel peut être modifié sans remise en cause majeure des développements

Fiabilité– le logiciel est robuste et fiable

Efficacité– cela est lié à la performance et dépend des caractéristiques propres au logiciel

11

11

LA TERMINOLOGIE

OBJETMETHODEMESSAGECLASSEHIERARCHIE DE CLASSE

12

12

OBJET

un objet comporte deux types de membres:– des données ou attributs– des fonctions ou méthodes

l'objet est un groupement d'attributs et de méthodes avec uneinterface parfaitement définie avec l'extérieurles objets communiquent entre eux par des messages, complétés ounon par des paramètresun sélecteur permet d'identifier le message

13

13

OBJET

l'objet récepteur du message déclenche une méthode lui appartenant correspondant au sélecteuril peut retourner à l'objet émetteur du message une donnée (qui peut être un objet)en Smalltalk, les messages sont traités par la bibliothèque d'exécution– les messages et les méthodes sont distincts

en C++ ou Java, les messages sont traités par le compilateur– un message est l'appel direct à une méthode

14

14

OBJET

La manipulation de plusieurs objets nécessite que chaque objet puisse être identifié– en Smalltalk, l'identificateur est un entier– en C++ ou Java, l'identificateur est l'adresse de l'objet

un objet possède:– un état (valeur de ses attributs)– un comportement (dicté par ses méthodes)– une identité (son nom)

la structure et le comportement d'objets similaires sont définis dans leur classe commune

15

15

CLASSE

une classe est un modèle pour la création d'objets et représente la nature des objets qu'elle permet de créerune classe définit:– les attributs– les méthodes– l'interface avec l'extérieur

les objets d'une même classe sont des instances de cette classe:– ils ont les mêmes méthodes– ils ont le même type d'attributs– ils peuvent avoir et ont en général des valeurs différentes pour leurs attributs

16

LES PROPRIETES DE L'APPROCHE OBJET

L'ENCAPSULATIONL'HERITAGELE POLYMORPHISME

17

17

L'ENCAPSULATION

l'encapsulation est le fait d'empaqueter ensemble données etfonctions avec une visibilité réduite depuis l'extérieurla mise en oeuvre de l'encapsulation est effectuée par les classesles attributs et méthodes sont masqués pour les objets qui n'ont pasbesoin d'y accéderle masquage est défini dans une classe par une interface qui précise quels sont les membres (attributs ou méthodes) accessibles depuis l'extérieur

18

18

L'ENCAPSULATION

le masquage des membres d'une classe permet d'utiliser les objets decette classe en ayant un accès limité à leurs membresles méthodes d'une classe peuvent être:– privées, donc inaccessibles par un autre objet– publiques, c'est-à-dire qu'un message provenant d'un autre objet peut les

déclencher

les attributs devraient toujours rester privés– C++ et Java autorisent les attributs à être publics, cependant il est

recommandé de laisser les attributs privés pour conserver une bonneencapsulation

19

19

L'HERITAGE

l'héritage traduit la relation EST-UN de notre langage– un employé est une personne– un employé hérite de personne

la relation EST-UN ne doit pas être confondue avec la relation A-UN qui est traduite par la composition– une personne a une tête, des jambes, des bras– il n'y a pas dans ce cas de relation d'héritage entre tête, jambes ou bras

20

20

L'HERITAGE

l'héritage met en relation au moins deux classes:– la classe du dessus est appelée classe parente ou classe de base ou super-

classe– la classe du dessous est appelée classe fille ou classe dérivée ou sous-classe

si une classe Employe hérite d'une classe Personne (car un EmployeEST-UNE Personne):– Personne est la classe de base ou super-classe– Employe est la classe dérivée ou sous-classe

21

21

L'HERITAGE

plusieurs classes peuvent hériter d'une même classe– la classe Employe hérite de la classe Personne– la classe Actionnaire hérite de la classe Personne

une même classe peut hériter de plusieurs classes de base– la classe Directeur hérite de la classe Employe et de la classe Actionnaire car

un Directeur EST-UN Employe ET un Actionnaire

22

22

L'HERITAGE

une classe dérivée hérite, c'est-à-dire bénéficie des biens de sa classede base:– de ses attributs– de ses méthodes

l'héritage est transitif, par conséquent une classe dérivée hérite de laclasse de base de sa classe de baseune hiérarchie de classe décrit les relations d'héritage entre plusieursclasses

23

23

L'HERITAGE

l'héritage apporte une technique de réutilisation:– ce qui a été développé pour une classe de base peut être réutilisé par ses

classes dérivées

l'héritage traduit la spécialisation si l'on considère les classesdérivées– un Employe est une Personne avec un salaire

il traduit la généralisation si l'on considère les classes de base– une Personne représente des caractéristiques et le comportement commun des

Employes et des Actionnaires

24

24

LE POLYMORPHISME

le polymorphisme est la propriété qui permet de manipuler desobjets sans tenir compte de leur classecette propriété découle de l'héritage et ne peut être mise en oeuvrequ'à travers ce type de relationle polymorphisme permet de manipuler un objet d'une classe dérivéeen le considérant comme un objet de sa classe de base, tout enconservant sa spécificité (comportement spécialisé)la mise en oeuvre du polymorphisme permet de réutiliser du code enapportant des modifications

25

LES PRINCIPAUX LANGAGES OBJETS

SMALLTALKEIFFELOBJECTIVE CADAC++JAVA

26

26

SMALLTALK

Smalltalk est l'un des langages orienté objet les plus populairesil a été développé au PARC (Palo Alto Research Center) de Xeroxdans les années 1970il s'agit d'un environnement de développementil comporte la modification dynamique des instances de classeil est assez utilisé outre-Atlantique, mais les liens dynamiques pénalisent les performances

27

27

EIFFEL

on doit ce langage à un français, Bertrand Meyeril s'agit d'un environnement orienté objet propriétaire pour stations UNIXil comporte une petite bibliothèque de classes prédéfiniesce langage supporte la gestion des exceptions, la généricité,l'héritage multiplela gestion mémoire est automatique mais peut être contrôléele compilateur traduit les programmes source en C

28

28

OBJECTIVE C

ce langage est compatible avec C et complète celui-ci par une approche objet simplifiéeson intégration avec C n'est pas aussi poussée que C++il adopte la syntaxe de Smalltalk et supporte l'héritage multiple et lesbibliothèques génériquesle système d'ecploitation NextStep de la société Next a été écrit en Objective C

29

29

ADA

le langage ADA fut développé sur appel d'offres du Department ofDefense (DoD) des Etats-Unis vers la fin des années 1970il fallait offrir un langage robuste et fiable à l'armée américainela proposition d'un français, René Ichbiack, fut retenue sa normalisation ANSI intervient en 1983la dernière version est ADA 95il s'agit d'un langage procédural, non purement objet, qui comporte des notions comme les packages, les types privés, les exceptions, la généricité, les tâches, l' héritage simple, les constructeurs etdestructeursson utilisation principale se situe dans le domaine militaire et aéro-spatial, pour le développement de grand projets

le nom ADA provient du nom de la contesse de Lovelace, Augusta Ada ByronAda fut assistant de Charles Babbage qui inventa la première machine à calculer

30

30

C++

le C++ a été développé aux laboratoires AT&T de Bell au début desannées 1980 par Bjarne Stroustrupil a été conçu pour être compatible avec le C, qu'il améliore sur denombreux pointsil s'agit d'un langage hybride, qui n'oblige pas à la programmation objetil ne comporte pas de bibliothèque de classes standardchaque éditeur fournit sa propre bibliothèque de classes

31

31

C++

il s'agit d'un langage puissant, comportant des classes, les exceptions, la généricité, l'héritage multiple, les constructeurs etdestructeurs, la surcharge d'opérateursla gestion de l'allocation dynamique de mémoire est entièrement à la charge du programmeurC++ est un langage complexe, mais efficace

32

32

JAVA

JAVA a été développé chez Sun Microsystems en 1991 pour ledéveloppement de logiciels enfouis concernant les appareils degrande consommation (télévision, magnétoscopes etc...)il est devenu rapidement un langage de prédilection pour la distribution de programmes exécutables via le World Wide Webil a pris son essor après le développement en 1994 du navigateur HotJava de Sun permettant l'exécution d'applets Java

33

33

JAVA

Java est fortement inspiré de C++ mais certaines fonctionnalités considérées commes dangereuses (pointeurs, héritage multiple) ou superflues (surcharge d'opérateurs) ont été abandonnées.Java apporte d'autres fonctionnalités comme le multithreadinginspiré du langage ADAJava comporte une bibliothèque de classes standard qui peut être complétée par d'autres classes

34

LE LANGAGE C++PRESENTATION DE C++CLASSES ET OBJETSOBJETS, TABLEAUX ET POINTEURSARGUMENTS PAR DEFAUTMOT-CLE THIS FONCTIONS ET METHODES INLINEVARIABLES ET METHODES DE CLASSELA NOTION DE REFERENCELE PASSAGE D'ARGUMENTSLE RETOUR D'OBJETSLES CONSTANTESALLOCATION DYNAMIQUE DE MEMOIRELE CONSTRUCTEUR DE COPIE

35

LE LANGAGE C++(Suite)LA SURCHARGE DE L'OPERATEUR=FONCTIONS ET CLASSES AMIESSURCHARGE D'OPERATEURSLES CLASSES INTERNESLA COMPOSITIONL'HERITAGELE POLYMORPHISMEIDENTIFICATION DE TYPE A L'EXECUTION (RTTI)LES EXCEPTIONSLA GENERICITE

36

PRESENTATION DE C++HISTORIQUECARACTERISTIQUES PRINCIPALES DU C++COMPILATIONUN C AMELIORECOMMENTAIRESCONSTANTESLE TYPE BOOLCONVERSIONSDEFINITION DE VARIABLESPROTYPAGE DE FONCTIONSEDITION DE LIENSLES ESPACES DE NOMSMOTS-CLES DU LANGAGEPRIORITE DES OPERATEURS

37

37

HISTORIQUE

Bjarne Stroustrup, l'auteur du C++, s'est inspiré du langage C et de Simula67 ainsi que d'Algol68la première version de son langage s'appelait "C with classes" en 1980cette première version a donné naissance en 1983 au langage C++la version 1.2 du C++ est sortie en 1987 et a rencontré un vif succès auprès des développeursles versions 2.0 puis 2.1 sortirent ensuite au début des années 1990 avec l'objectif d'une normalisation du langagela version 3.0, sortie en 1998, se veut être la version ISO du langage

38

38

CARACTERISTIQUES PRINCIPALES DU C++

le C++ est un langage hybride compatible avec le langage C, permettant la réalisation de programmes objets, mais autorisant aussi la réalisation de programmes non objetsC++ est sans aucun doute le langage le plus complexe, le plus puissant aussi, permettant la réalisation de programmes très rapides à l'exécutionil s'agit d'un langage réservé à des spécialistes qui ne peuvent pas utiliser un autre langage pour les raisons suivantes:– nécessité d'une grande rapidité à l'exécution– nécessité ou souhait d'employer un langage objet– compatibité nécessaire avec des logiciels existants en C ou C++

39

39

CARACTERISTIQUES PRINCIPALES DU C++

des langages objets plus récents sont en effet disponibles, comme Java ou C#, offrant globalement les mêmes possibilités pour une plus grande simplicitéles domaines d'utilisation du C++ sont très nombreux, citons:– l'informatique industrielle (temps réel)– l'informatique scientifique (calculs)– l'informatique des salles de marché et de la finance en général (IHM+calculs)

40

40

COMPILATION

la compilation d'un programme C++ s'effectue comme celle d'un programme C, à l'aide d'un compilateur C++sous LINUX, le compilateur GNU gcc intègre un compilateur C++ que l'on peut invoquer par la commande: g++avec le compilateur gcc, les fichiers sources C++ doivent avoir une extension .C, .cc, .cx ou .cppsous l'environnement Windows, des compilateurs commerciaux comme Visual C++ de Microsoft, ou Borland C++ de Borland, nécessitent des fichiers sources avec extension .cpp

41

41

UN C AMELIORE

le langage C, développé initialement par Brian Kernighan et Dennis Ritchie en 1972, a été normalisé en 1989 et est alors devenu le C ANSIle langage C++ a, quant à lui, été mis au point au début des années 1980 par Bjarne Stroustrup, lequel a voulu englober le langage C en lui apportant toutefois quelques amélioration substantiellesBjarne Stroustrup a "saupoudré" le langage C de nombreuses améliorations en l'introduisant dans le C++, conduisant ainsi à un C amélioré, avec bien sûr une couche objet en plusune liste non exhaustive des améliorations est décrite ci-après, la plupart étant approfondies dans la suite du cours

Toute représentation ou reproduction intégrale ou partielle faite sans le consentement de l'auteur Denis MADEC ou de ses ayants droits ou ayant cause est illicite selon le code de la propriété intellectuelle du 1er juillet 1992 et constitue une contefaçon réprimée par le code pénal.

42

42

UN C AMELIORE– Commentaires– Constantes– Le type bool– Conversions– Définition de variables– Prototypage des fonctions– Édition de liens– Espaces de noms– Surcharge de fonctions– Arguments par défaut– Fonctions inline– Références– Allocation dynamique de mémoire– Fonctions génériques– Entrées/sorties sous forme d'objets

43

43

COMMENTAIRES

en plus des symboles /*…*/ utilisés en C, un commentaire peut être introduit en C++ par //il prend fin automatiquement en fin de ligne

/* programme de …. . .

*/int main() { // fonction principale

// renvoie 0 si pas d'erreur. . .}

en aucun cas les commentaires ne doivent être imbriqués

44

44

CONSTANTES

en C, les constantes litérales peuvent être définies de deux manières– par la directive #define du pré-processeur– par le mot-clé const devant une définition de variable

const double pi=3.14159;

l'emploi de #define est déconseillé en C++ car il s'agit d'une simple substitution effectuée par le pré-processeur que le compilateur ne peut contrôlerC++ encourage la définition de constantes utilisant le mot-clé constcar il améliore leur mise-en-œuvre: – une constante ne réserve pas nécessairement de la mémoire– une constante exige une valeur d'initialisation– elle a comme portée le fichier dans laquelle elle est définie

45

45

LE TYPE BOOL

un nouveau type a fait son apparition en C++: le type boolil permet de représenter une variable ne prenant que deux valeurs possibles: true, falseles opérateurs relationnels renvoient un boolun bool peut être converti en int et un int en bool: true correspond à 1, false à 0 et réciproquement

46

46

CONVERSIONS

le C est très tolérant quant aux opérations effectuées sur les données, notamment en ce qui concerne les conversionsC++ contrôle le type des données d'une façon plus stricte, nécessitant parfois une conversion explicite là ou le C ne l'impose pas

double x;int a;//a=x; // accepté en C, refusé en C++a=(int)x; // accepté en C, accepté en C++

de façon à rendre encore plus sûres les conversions explicites, une nouvelle syntaxe a été mise en place, laissant toutefois le compilateur accepter l'ancienne notation

47

47

CONVERSIONS

il existe dorénavant quatre sortes de conversion explicite:static_cast, dynamic_cast, reinterpret_cast et const_cast– la syntaxe est la suivante: static_cast<type>(donnée)

static_cast permet de convertir un type vers un type plus faible, par exemple un long en int ou un double en int, ou un pointeur sur charen pointeur sur int– par exemple:

double x;int a;a=static_cast<int>(x); // ou a=(int)x;

48

48

CONVERSIONS

dynamic_cast est utilisé lorsque l'on manipule des objets dérivés via des pointeurs ou des références (Cf chapitre "Identification de type à l'exécution)reinterpret_cast est utilisé pour des conversions extrêmes, par exemple un int vers un pointeur, nécessitant une double interprétation:

int a;int *p=reinterpret_cast<int*>(a);

const_cast est utilisé uniquement pour ajouter mais surtout enlever le spécificateur const à un type

const char* p="DMA";char *d=const_cast<char*>(p);

49

49

DEFINITION DE VARIABLES

en C, les variables locales doivent obligatoirement être définies en début de blocen C++, une variable locale peut être définie n'importe où dans un bloc, et utilisable à partir de l'endroit où elle a été définiececi s'applique particulièrement aux fonctions

void f() {int a=3;a++;int b=5;b*=a;. . .

}

50

50

DEFINITION DE VARIABLES

ceci permet également de définir une variable au sein d'une boucle, cette variable n'existant plus en dehors de la boucle

for(int i=0;i<10;i++) cout<<i<<endl;

}// i n'existe plus ici!while(int a=getValeur()) {. . .}// a n'existe plus ici!

51

51

PROTOTYPAGE DES FONCTIONS

en l'absence du prototype d'une fonction f avant son appel, le C suppose qu'elle a été déclarée comme: int f(void)le C++ renforce le contrôle lors de l'appel d'une fonction en exigeant sa déclaration préalable (appelée prototype) de manière explicite

double calcul(int, double); // déclaration obligatoire en C++

int main() {. . .double x=calcul(5, 78.65);. . .

}

52

52

EDITIONS DE LIENS

l'édition de liens C++ met en œuvre le "type safe linkage"les fichiers objets générés par le compilateur C++ comportent les noms des fonctions sous forme encodée de façon à permettre la distinction entre fonctions surchargéespour lier des fonctions C avec du code C++, et compte tenu des différences de format entre les fichiers objets C et C++, il faut indiquer quelles sont les fonctions C utilisées dans le code source C++ceci s'effectue au moyen d'une directive extern "C" qu'il faut placer en dehors de toute fonction, de préférence dans un fichier d'entête

53

53

EDITIONS DE LIENS

entre double quotes figure le langage dans lequel les fonctions appelées ont été compilées (il s'agit en fait de la forme de l'édition de liens, "C" convenant également pour le Fortran ou l'assembleur par exemple)extern "C" void calcul(double,double); // déclaration d'une fonction Cextern "C" { // déclaration de plusieurs fonctions C

int f(int);float g(double);

}extern "C" { // déclaration de plusieurs fonctions C

#include <string.h> // d'une bibliothèque}

54

54

EDITIONS DE LIENS

extern "C" { // déclaration de plusieurs fonctions Cint f(int);float g(double);

}int main() {

int b;double x;. . .int a=f(b);double z=g(x);

}

55

55

LES ESPACES DE NOMS

les espaces de noms (namespaces) ont pour objectif principal de résoudre les conflits portant sur le nom donné aux identificateurs dans un programme C++on peut dès lors envisager de donner à une classe le même nom qu'une classe de la bibliothèque par exemplela syntaxe d'un espace de noms est la suivante:

namespace identificateur {. . .}

tout ce qui se trouve entre les accolades fait partie de l'espace de noms appelé identificateur

56

56

LES ESPACES DE NOMS

toute classe, fonction, variable de cet espace de nom doit être préfixée par le nom de l'espace suivi de :: pour être trouvée par le compilateur

namespace alpha {class A {};. . .

}int main() {

alpha::A aa;}

57

57

LES ESPACES DE NOMS

en pratique, cette écriture est principalement utilisée pour lever des ambiguités, la directive using namespace étant plus facile d'emploi

namespace alpha {class A {};. . .

}using namespace alpha;int main() {

A aa;}

58

58

LES ESPACES DE NOMS

namespace alpha {class A {};

}namespace beta {

class A {};

}using namespace alpha;int main() {

A aa; // il s'agit de alpha::Abeta::A b;

}

59

59

LES ESPACES DE NOMS

la bibliothèque standard du C++ fournie avec tout compilateur est définie dans l'espace de nom std, qu'il faut donc indiquer pour utiliser un élément de cette bibliothèque

#include <iostream>#include <string>using namespace std;

int main() {ifstream ficin("alpha.txt");. . .

}

60

60

MOTS-CLES DU LANGAGE

asm auto bool break case catch char class const const_cast continue default delete do double dynamic_cast else enum explicit extern false float for friend int long goto if inline int long mutable namespace new operator private protected public register reinterpret_cast return short signed sizeof static static_cast struct switch template this throw true try typedef typeid typename union unsigned using virtual void volatile wchar_t while

bien que non utilisés, les mot-clés suivants sont également réservés:and and_eq bit_and bit_or compl export not not_eq or or_eq xor xor_eq

61

61

PRIORITE DES OPERATEURS

évalués en premier:: de gauche à droite. -> [] () lvalue++ lvalue -- typeid() xxx_cast de gauche à droite! ~ + - (cast) ++lvalue --lvalue *expr &lvalue sizeof new delete de droite à gauche

.* -> de gauche à droite* / % de gauche à droite+ - de gauche à droite<< >> de gauche à droite< > >= <= de gauche à droite== != de gauche à droite& de gauche à droite^ de gauche à droite| de gauche à droite&& de gauche à droite|| de gauche à droite= += -= *= /= %= ^= &= |= <<= >>= de droite à gauche?: de gauche à droitethrow de gauche à droite, de gauche à droite

62

INTRODUCTION AUX ENTREES/SORTIES

63

63

INTRODUCTION AUX ENTREES/SORTIES

bien que les entrées/sorties du langage C continuent d'être supportées, il est préférable d'utiliser les objets prédéfinis du C++, ou d'utiliser les classes d'E/S disponibles pour créer ses propres obetsil existe 3 objets prédéfinis cin, cout, cerr correspondant respectivement aux fichier standard d'entrée, fichier standard de sortie, fichier standard d'erreurpar défaut, ces fichiers standards sont associés au clavier pour cin, à l'écran pour cout et cerrla mise en œuvre de ces objets s'effectue en utilisant l'opérateur <<pour cout et cerr, >> pour cindans tous les cas, il faut inclure le fichier iostream.h qui décrit ces objets

64

64

INTRODUCTION AUX ENTREES/SORTIES

#include <iostream.h>

int main() {int x;double z;

cout<<"saisir une valeur entière:";cin>>x;cout<<"saisir une valeur décimale:";cin>>z;cout<<"valeurs saisies: "<<x<<", "<<z<<endl; // endl: end of linereturn 0;

}

65

65

EXERCICES

Votre environnement de travail vous est présenté par votre formateur. Il vous indique comment réaliser les exercices, comment compiler et exécuter vos programmes

Exo1: premier programme C++ simple

Exo2: affichage de variables de types primitifs

Exo3: saisie d'un nombre décimal, calcul, affichage du résultat

66

CLASSES ET OBJETSLA DEFINITION DE CLASSEL'INSTANCIATION D'UNE CLASSEL'APPEL DES METHODESL'ACCES AUX MEMBRESLA SURCHARGE DE METHODESLES CONSTRUCTEURSLE DESTRUCTEUR

67

67

LA DEFINITION DE CLASSE

une classe est un modèle pour la création d'objetselle comporte des données membres ou attributs ou données d'instanceelle comporte des fonctions membres ou méthodeselle définit l'interface d'accès avec l'extérieur pour ses membresles attributs d'une classe peuvent être des types de base, des objetsil peut ne pas y avoir d'attributs dans une classe (seulement desméthodes)il peut ne pas y avoir de méthodes (seulement des variables)par convention, le nom d'une classe commence par une majuscule

68

68

LA DEFINITION DE CLASSE

class Counter { // nom de la classeprivate:

int count; // attributpublic:

int increment() { // méthodereturn ++ count;

}void affiche() { // méthode

cout<< "valeur : " << count<<endl; }

}; // point-virgule obligatoire

69

69

L' INSTANCIATION D'UNE CLASSE

"instancier" une classe signifie créer un objet sur le modèle de cette classe– un objet est une instance de classe

tous les objets de cette même classe auront un comportement identiquetous les objets de cette classe auront les mêmes types d'attributs– chaque objet comportera ses propres attributs

les objets d'une même classe pourront avoir des états différentsil faut tout simplement créer une variable du type de la classe:

Counter c1; // c1 est une instance de la classe Counter

70

70

L' APPEL DES METHODES

une méthode doit être appelée (invoquée) pour un objet donné:

Counter c1;c1.affiche();int x = c1.lirecount();cout<<"compteur c1 : "<< c1.increment()<<endl;

71

71

L' ACCES AUX MEMBRES

les méthodes d'une classe ont toujours directement accès aux autres membres de cette même classe (variables ou méthodes)l'interface de la classe définit l'accès à ses membres depuis l'extérieurde la classe, c'est-à-dire depuis une autre classel'interface d'accès aux membres d'une classe est défini par les mots-clés:

public, protected, private

ces mots-clés définissent des sections dans lesquelles sont placés lesmembres de la classepar défaut, en l'absence de toute indication, c'est le spécificateur private qui est utilisé

72

72

L' ACCES AUX MEMBRES

public– un membre public est accessible de tout autre objet d'une autre classe

protected– un membre protected est inaccessible de tout objet d'une autre classe sauf

d'une classe dérivée

private– un membre private interdit son accès depuis tout objet d'une autre classe

73

73

L' ACCES AUX MEMBRES

un bon programme orienté objets doit conserver l'encapsulation desdonnéesles variables doivent donc rester privées dans la mesure du possibleles méthodes permettant d'y accéder seront publiquesbien que non recommandé, il est possible d'accéder aux attributs, si l'interface défini dans la classe le permet:

c1.count = 0; // ok si count accessible

74

74

EXERCICE

Exo4: définition d'une classe, création d'objets de cette classe, ajout d'une méthode

75

75

LA SURCHARGE DE METHODES

le prototype d'une méthode (d'instance ou de classe) est composé de:– son nom– le type de chacun des arguments qu'elle reçoit– son type de retour

la signature d'une méthode est composée de:– son nom– le type de chacun des arguments qu'elle reçoit

76

76

LA SURCHARGE DE METHODES

la surcharge de méthodes (d'instances ou de classe) consiste à donnerle même nom à des méthodes différentes dans une même classele compilateur les différencie par leur signature (nombre et type des arguments)le type de retour n'intervient pas dans la surchargeil s'agit d'une facilité d'écritureil n'y a pas de limites aux nombres de surcharges d'une même méthode

77

77

LA SURCHARGE DE METHODES

class Date {private:

int jour;char mois[20];int annee;

public:void setDate (int j, char* m, int a) {

. .}void setDate (int numero_jour) { // méthode surchargée. .}

};

78

78

LA SURCHARGE DE METHODES

int main(){

int jour;Date d;

. . .d.setDate(30, « mars »,1957); // il s’agit de la méthode setDate(int,char*,int). . .d.setDate(128); // il s’agit de la méthode setDate(int)return 0;

}

79

79

EXERCICE

Exo5: surcharge de méthode dans la classe Counter

80

80

LES CONSTRUCTEURS

un constructeur est une méthode automatiquement invoquée lors de la création d'un objet– un constructeur n'est invoqué qu'une seule fois dans la vie d'un objet– il sert essentiellement à initialiser les attributs

bien que non indispensable, un constructeur permet la création d'un objet en précisant des valeurs initiales pour ses attributsles constructeurs peuvent être surchargés – le langage fournit systématiquement un constructeur dit "constructeur par

défaut"– un constructeur explicite sans arguments remplace le constructeur par défaut– s'il existe un constructeur avec argument(s), alors la construction d'un objet

par défaut nécessitera la présence d'un constructeur explicite sans argument

81

81

LES CONSTRUCTEURS

un constructeur doit respecter les règles suivantes:– son nom est obligatoirement celui de sa classe– il ne doit pas comporter de type de retour (même void)– il ne peut être appelé explicitement– il doit être accessible (donc public en général)

un constructeur ne peut être explicitement invoqué pour un objet, il s'agit d'une méthode réservée pour le compilateurun constructeur se doit d'être accessible depuis la méthode danslaquelle on crée l'objet . Il est en général public

82

82

LES CONSTRUCTEURS

class Counter {int count;public:

Counter() { count=0; } // constructeur sans argumentCounter(int val) { count=val; } // constructeur avec argument. . .

};void main() {

Counter c1; // appel du constructeur sans argumentCounter c2(10); // appel du constructeur avec argumentc1.affiche(); // affiche 0c2.affiche(); // affiche 10

}

83

83

LES CONSTRUCTEURS

autre notation possible en utilisant une liste d'initialisateurs de membres:class Counter {

int count;public:

Counter():count(0) { } // constructeur sans argumentCounter(int val):count(val) { } // constructeur avec argument. . .

};cette notation est recommandée, et devient obligatoire lorsque des attributs constants ou de type référence (Cf chapitre "Référence") doivent être initialisés

84

84

EXERCICE

Exo6: mise en place de trois constructeurs dans la classe Counter

85

85

LE DESTRUCTEUR

un destructeur est une méthode automatiquement invoquée juste avant la disparition d'un objetsa présence est nécessaire lorsque certaines ressources telles que la mémoire, des fichiers, des sockets qu'il faut libérer sont utilisés dans l'objetune classe ne peut comporter qu'un seul destructeurson nom est constitué du nom de sa classe précédé du caractère ~

~Date(){. .

}

un destructeur peut être appelé explicitement

86

86

EXERCICE

Exo7: mise en place d'un destructeur dans la classe Counter

87

87

LA DEFINITION DE CLASSE

en pratique, la définition monolithique d'une classe dans un même fichier source pose des problèmes lors de la compilation ou de l'édition de liens de programmes comportant de nombreux fichiersil est préférable de séparer la définition de la classe de la définition des méthodesla classe est alors définie dans un fichier d'entête .h et les méthodes dans un fichier sourcele fichier d'entête pourra être inclus (directive #include du pré-processeur) dans tout fichier source ayant besoin d'utiliser le type correspondant

Toute représentation ou reproduction intégrale ou partielle faite sans le consentement de l'auteur Denis MADEC ou de ses ayants droits ou ayant cause est illicite selon le code de la propriété intellectuelle du 1er juillet 1992 et constitue une contefaçon réprimée par le code pénal.

88

88

LA DEFINITION DE CLASSE

fichier counter.h

class Counter { private:

int count; public:

int increment();void affiche();

};seuls les prototypes des méthodes figurent dans cette définition de la classe

89

89

LA DEFINITION DE CLASSE

fichier counter.cpp#include "counter.h"

int Counter::increment() { return ++ count;

}void Counter::affiche() {

cout<< "valeur : " << count<<endl; }

90

90

LA DEFINITION DE CLASSE

les méthodes sont définies en indiquant leur nom précédé de leurclasse et suivi de l'opérateur ::

int Counter::increment() { return ++ count;

}il doit y avoir cohérence totale entre les éléments définis dans la classe et les méthodes définies dans le fichier sourcedans la suite de ce cours et par commodité, les classes seront le plus souvent définies de façon monolithique

91

91

EXERCICE

Exo8: définir la classe Counter dans un fichier d'entête et ses méthodes dans un fichier source

92

OBJETS, TABLEAUX ET POINTEURS

93

93

OBJETS, TABLEAUX ET POINTEURS

les objets peuvent avantageusement être manipulés via des pointeurs:

Counter c1(4), c2(8);Counter *p=&c;p->increment();p->affiche();p=&c2;p->affiche();

94

94

OBJETS, TABLEAUX ET POINTEURS

les tableaux d'objets peuvent être simplements créés en utilisant la notation usuelle:

Counter tab[10]; // tableau tab de 10 objets Counter

le constructeur par défaut ou le constructeur sans argument est automatiquement appelé pour l'initilisation des objets du tableau

for (int i=0 ; i<10 ; i++)tab[i].affiche();

OU Counter *p;for (p=tab,int i=0 ; i<10 ; i++,p++)

p->affiche();

95

ARGUMENTS PAR DEFAUT

96

96

ARGUMENTS PAR DEFAUT

les arguments par défaut d'une fonction ou d'une méthode permettent, lors de l'appel, de ne préciser qu'une partie des arguments, les autres prenant une valeur par défaut indiquée dans la déclaration de la fonction ou méthodela valeur des arguments par défaut doit être précisée dans la déclaration de la fonction ou méthode et non dans sa définitionseuls les derniers arguments consécutifs d'une fonction ou méthode peuvent être des arguments par défauttous les arguments d'une fonction ou méthode peuvent être des arguments par défautune fonction ou méthode comportant des arguments par défaut peutêtre surchargée, mais certaines ambiguités peuvent surgir

97

97

ARGUMENTS PAR DEFAUT

char* convert(int, int =10); // déclaration: la base est 10 par défaut

char* convert(int x, int base) { // conversion en chaine de l'entier x dans. . . // la base indiquée (2, 8, 10 ou 16)

}

int main() {int a=10;cout<<"base 10: "<<convert(a)<<endl;cout<<"base 8: "<<convert(a,8)<<endl;. . .

}

98

98

ARGUMENTS PAR DEFAUT

la présence d'arguments par défaut permet souvent de réduire le nombre de méthodes surchargées

class Counter {int count;public:

Counter(int =0); // remplace Counter() et Counter(int). . .

};

99

99

EXERCICE

Exo9: mettre en place une méthode comportant un argument par défaut dans la classe Counter

100

LE MOT-CLE THIS

101

101

LE MOT-CLE THIS

les méthodes d'instance, ou méthode, d'une classe définissent lecomportement des objets de cette classeellles permettent d'accéder aux attributs, aux variables de classe,d'effectuer des traitements à partir d'arguments etc...elles doivent nécessairement être invoquées pour un objet de laclasse

102

102

LE MOT-CLE THIS

this est un mot-clé qui représente l'objet courant, c'est-à-dire l'objetpour lequel la méthode a été invoquée, au sein d'une méthode d'instance– this est un pointeur constant sur l'objet courant

dans une méthode d'instance, il peut être utilisé pour:– retourner l'objet courant– le passer en argument à une autre méthode– différencier les attributs de variables locales de mêmes noms– éviter des traitements inutiles ou impossibles

103

103

LE MOT-CLE THIS

utilisation de this pour retourner l'objet courant:class Counter {

int count; public:

Counter increment() {count++; return *this; // l'objet courant est retourné

}void affiche() {

cout<<"valeur : "<<count<<endl; }

};

104

104

LE MOT-CLE THIS

utilisation de this pour retourner l'objet courant (suite):

int main() { Counter c1;c1.increment().affiche(); // valeur: 1. . .

}

105

105

LE MOT-CLE THIS

utilisation de this pour transmettre l'objet courant en argument:

class Alpha{Beta b;. . .public:

void start() {b.calcul(*this);. . .

};

106

106

LE MOT-CLE THIS

utilisation de this pour différencier les attributs de variables locales de mêmes noms:class Counter {

int count; public:

void init(int count) {this->count=count; // count désigne l'argument

// this->count désigne l'attribut}void affiche() {

cout<<"valeur : "<<count<<endl; }

};

107

107

LE MOT-CLE THIS

utilisation de this pour éviter des traitements inutiles ou impossibles:

class Personne {. . . public:void mariage(Personne* p) {

if(this != p){// traitement

}else{// erreur: on ne peut se marier avec soi-même

}};

108

108

EXERCICE

Exo10: retour de l'objet courant dans quelques méthodes de la classe Counter

109

FONCTIONS ET METHODES INLINE

110

110

FONCTIONS ET METHODES INLINE

une fonction ou une méthode inline correspond en fait à une macro-instructionune telle fonction ou méthode doit comporter le mot-clé inlineprécédant sa définition

inline int max (int a, int b) {return a>b?a:b;

}int main(){

int x=3,y=5,z;z=max(x,y);

}

111

111

FONCTIONS ET METHODES INLINE

l'intérêt d'utiliser une macro-instruction en lieu et place d'une fonction/méthode est d'éviter les phases d'appel et de retour qui nécessitent une sauvegarde/restauration du contexte de la fonction/méthode appelante en pilesi la fonction/méthode appelée réalise peu de traitements, la sauvegarde/restauration du contexte de la fonction/méthode appelante peut parfois représenter l'essentiel du travail réalisé, pénalisant ainsi l'opération globaleen utilisant une méthode inline, le compilateur substitue l'appel à la fonction/méthode par les instructions correspondant à la fonction/méthode inline, évitant ainsi les phases d'appel et de retour

112

112

FONCTIONS ET METHODES INLINE

en pratique, il s'agit d'une technique d'optimisation qui présente quelques inconvénients:– le compilateur ne peut traiter une fonction/méthode inline que si elle est

utilisée dans le même fichier que celui dans lequel elle est définie, la vraie solution consistant à la placer dans un fichier d'entête

– la présence du mot-clé inline devant une fonction/méthode ne garantit pas son traitement comme macro-instruction

– une méthode inline ne peut être virtuelle (Cf chapitre sur le polymorphisme)

dans le cas d'une définition de classe monolithique, les méthodes sont par défaut inlinedans le cas d'une définition qui sépare les méthodes de leur classe, le mot-clé inline est requis devant la définition des méthodes dont on souhaite qu'elles soient traitées comme des macro-instructions

113

113

FONCTIONS ET METHODES INLINE

class Counter {int count;

public:int increment() { // méthode implicitement inline

return ++ count; }void affiche() { // méthode implicitement inline

cout<< "valeur : " << count<<endl; }int lirecount() { // méthode implicitement inline

return count; }

};

114

114

FONCTIONS ET METHODES INLINE

class Counter { private:

int count; public:

int increment();void affiche();int lirecount();

};

115

115

FONCTIONS ET METHODES INLINE

inline int Counter::increment() { return ++ count;

}inline void Counter::affiche() {

cout<< "valeur : " << count<<endl; }inline int Counter::lirecount() {

return count; }

116

116

EXERCICE

Exo11: définir inline quelques méthodes de la classe Counter

117

LES VARIABLES ET METHODES DE CLASSE

LES VARIABLES DE CLASSELES METHODES DE CLASSE

118

118

LES VARIABLES DE CLASSE

les variables de classe sont "globales" à une même classeleurs valeurs sont les mêmes pour tous les objets de cette classeelles ne se retrouvent pas dans chacun des objets de cette classeil s'agit donc de variables partagées par tous les objets d'une classecomme les variables d'instance, elles bénéficient des accès private, protected, public

119

119

LES VARIABLES DE CLASSE

le mot-clé static déclare une variable comme étant une variable declasseclass Article {

public:static char monnaie[32]; // variable de classe

private:static int nombreArticles; // variable de classedouble prix; // attributint quantite; // atrributchar designation[32]; // attribut

. . .};

120

une variabl e de classe respecte les règles d'accès comme les variables d'instance et les méthodes.

une variable de classe peut être invoquée soit via la classe, soit via un objet de cette classe. Cette dernière solution peut néanmoins prêter à confusion car une variable de classe n'est pas liée à un objet donné:

class Stock {public static void main (String args[]) {

Article a1 = new Article ();. . .

Article.nombreArticle = 100;. . .

a1.nombreArticle = 0;}

}

120

LES VARIABLES DE CLASSE

une variable de classe doit par ailleurs être définie, éventuellement initialisée:

int Article::nombreArticles = 0;lorsqu'une variable de classe est en accès public, elle peut être invoquée depuis l'extérieur de la classe de deux façons:– via un objet de cette classe comme pour un attribut public:

Article a1;strcpy(a1.monnaie,"F");

– via le nom de la classe (solution préférable):strcpy(Article::monnaie,"F");

l'accès à une variable de classe depuis une méthode de cette classe s'effectue comme pour un attribut

121

121

LES METHODES DE CLASSE

les méthodes de classe ne concernent pas un objet en particulier mais plutôt la classe elle-même, ou bien tous les objets de cette classele mot-clé static définit une méthode comme étant une méthode declasseune méthode de classe ne peut pas utiliser this, puisqu'elle n'est pasliée à un objet donnéelle ne peut pas accéder directement (sans préciser un objet) auxattributs ni aux méthodes d'instance, pour la même raisonelle peut accéder aux variables de classe (c'est son rôle principal) ouà d'autres méthodes de classe

122

une méthode de classe respecte les règles d'accès comme les variables d'instance et les autres méthodes.

une méthode de classe peut être invoquée soit via la classe, soit via un objet de cette classe. Cette dernière solution peut néanmoins prêter à confusion car une méthode de classe n'est pas liée à un objet donné:

class Stock {public static void main (String args[]) {

Article a1 = new Article ();. . .

int x = Article.lire_nombreArticle();. . .

int z = a1.lire_nombreArticle();}

}

122

LES METHODES DE CLASSE

class Article {private:

static int nombreArticles; // variable de classeint code; // attributchar designation[32]; // attribut

public:static int lire_nombreArticles () { // méthode de classe

return nombreArticles;}. . .

};

123

123

LES METHODES DE CLASSE

les méthodes de classe peuvent être invoquées de deux façons différentes:– via un objet de cette classe comme pour une méthode d'instance publique:

Article a;cout<<"Nombre d'articles : "<<a.lire_nombreArticles()<<endl;

– via le nom de la classe (solution préférable):cout<<"Nombre d'articles : " <<Article::lire_nombreArticles()<<endl;

l'accès à une méthode de classe depuis une méthode d'instance decette classe peut s'effectuer directement

124

124

EXERCICE

Exo12: définir une variable de classe et une méthode de classe dans la classe Counter

125

LA NOTION DE REFERENCE

126

126

LA NOTION DE REFERENCE

le langage C++ introduit une possibilité supplémentaire de manipuler les variables: la référenceune référence sur une variable est une sorte de pointeur implicite sur cette variable à la différence d'un pointeur cependant, une référence est définitivement liée à une variable, et ce, dès sa création

int x=6;int &rx=x; // rx est une référence sur xrx++; // x est modifié

le moyen le plus commode de considérer une référence est de la voir comme un alias

127

127

LA NOTION DE REFERENCE

les références sont beaucoup utilisées en C++ car elles facilitent la lisibilité tout en apportant une grande efficacitéelles sont notamment employées lors de la transmission d'objets en arguments de fonctions ou méthodeselles permettent également de manipuler des objets avec une grande efficacitéelles peuvent être utilisées également en attributen règle générale et pour des raisons d'efficacité, les objets devraient toujours être manipulés par référence (ou par pointeurs) sauf bonne(s) raison(s)

128

LE PASSAGE D'ARGUMENTS

129

129

LE PASSAGE D'ARGUMENTS

par défaut, les arguments des types primitifs et les objets sonttransmis aux méthodes ou aux fonctions par valeur (ou par copie)– une méthode ou fonction appelée ne peut modifier la donnée de la méthode

appelante

les arguments de type tableau sont transmis aux méthodes paradresse:– toute modification de ces arguments dans la méthode appelée se répercute sur

les objets initiaux de la méthode appelante

le passage d'arguments par référence permet d'obtenir les mêmes effets que ceux obtenus avec les pointeurs, tout en simplifiant l'écriture

130

130

LE PASSAGE D'ARGUMENTS

argument de type objet

void incr(Counter& c) {c.increment(); // c représente c1

}

int main () {Counter c1(5);incr(c1); // l'objet c1 est transmis par référencec1.affiche(); // apres l'appel, c1 est modifié

}

131

131

LE PASSAGE D'ARGUMENTS

argument de type primitif

void carre(int& c) {c*=c; // c représente i

}

int main () {int i = 5;carre(i); // la variable i est transmise par référence

// apres l'appel, i vaut 25}

132

132

EXERCICE

Exo13: transmission d'objets Counter par référence à une méthodede la classe Counter

133

LE RETOUR D'OBJETS

134

134

LE RETOUR D'OBJETS

par défaut, les données des types primitifs et les objets sont retournéspar valeur– il faut donc réaliser une affectation pour récupérer la valeur retournée

les tableaux retournés par les méthodes ou fonctions le sont paradresseon peut vouloir retourner certaines données par référence, notamment des objets pour des raisons d'efficacité

135

135

LE RETOUR D'OBJETSclass Counter {

int count; public:

Counter& add(int c) {count+=c;return *this; // retour de l'objet courant

}void affiche() { cout<<"valeur : " <<count<<endl; }

};int main () {

Counter c1(5);c1.add(4).add(6); // le retour d'objets par référence permet l'associativitéc1.affiche(); // affiche 15

}

136

136

EXERCICE

Exo14: retour d'objets par référence dans quelques méthodes de la classe Counter

137

LES CONSTANTES

138

138

LES CONSTANTES

en C++, le mot-clé const définit une constanteune valeur initiale doit nécessairement être attribuée à une constante lors de sa définitionclass Personne {

static const char* m = "Masculin";static const char* f = "Feminin";. . .

};l'utilisation des variables const est plus large qu'en C:

const int TAILLE=10;int tab[TAILLE]; // légal en C++, illégal en C

139

139

LES CONSTANTES

le mot-clé const est beaucoup utilisé lors du passage d'arguments par adresse ou par référenceil permet de se protéger d'une modification intempestive de l'argument par la fonction ou la méthodeclass Counter {

int count; public:

void add(const Counter& c) { // c ne peut être modifié car déclaré constcount+=c;

}void affiche() {

cout<<"valeur : " <<count<<endl; }

};

140

140

LES CONSTANTES

un objet constant est un objet dont l'état ne peut être modifiéconst Counter c(5);

seules les méthodes déclarées const peuvent être invoquées sur des objets constantsune méthode peut être déclarée const si elle ne modifie pas l'état des attributsil faut donc déclarer le maximum de méthodes const car l'apparition d'objets constants est quasiment inévitable

141

141

LES CONSTANTES

class Counter { int count;

public:void add(const Counter& c) {// c ne peut être modifié car déclaré const

count+=c;c.affiche(); // ok car affiche() méthode const

}void affiche() const {

cout<<"valeur : " <<count<<endl; }

};

142

142

LES CONSTANTES

lorsqu'un attribut est constant, son initialisation doit avoir lieu dans la liste d'initalisateurs de membres

class Counter { int count;const int MAX;

public:Counter():count(0), MAX(10000){} // OK

// Counter():count(0){ MAX=10000;} illégal

. . .};

143

143

EXERCICE

Exo15: ajout du mot-clé const sur quelques méthodes et sur certains arguments de méthodes

144

ALLOCATION DYNAMIQUE DE MEMOIRE

Toute représentation ou reproduction intégrale ou partielle faite sans le consentement de l'auteur Denis MADEC ou de ses ayants droits ou ayant cause est illicite selon le code de la propriété intellectuelle du 1er juillet 1992 et constitue une contefaçon réprimée par le code pénal.

145

145

ALLOCATION DYNAMIQUE DE MEMOIRE

l'allocation dynamique de mémoire permet de construire des applications qui n'utilisent que la quantité de mémoire dont elles ont besoin à un instant donnél'objectif est d'optimiser la gestion de la ressource mémoire, lorsque les besoins risquent d'être importants compte-tenu de la quantité de mémoire disponibleen C, les fonctions standards malloc et calloc permettent d'allouer une zone mémoire, realloc permet de réallouer et free permet de libérer une zone mémoire préalablement allouée

146

146

ALLOCATION DYNAMIQUE DE MEMOIRE

en C++, l'allocation s'effectue via les opérateurs new et new[], la libération par les opérateurs delete et delete[]il y a incompatibilité entre les fonctions d'allocation/libération du C et les opérateurs new, new[] et delete, delete[] du C++. Il est donc préférable de ne pas les utiliser conjointement au sein d'une même applicationtoute allocation effectuée à l'aide de l'opérateur new doit être libérée à l'aide de l'opérateur deletetoute allocation effectuée à l'aide de l'opérateur new[] doit être libérée à l'aide de l'opérateur delete[]la mémoire allouée par new ou new[] n'est pas initialisée

147

147

ALLOCATION DYNAMIQUE DE MEMOIRE

allocation d'une donnée simple:int *p=new int; // allocation dynamique de mémoire pour un entier*p=5;. . .delete p; // libération de la mémoire associée à cet entier

allocation d'un tableau:int *psav;int *pt=new int[10]; // alloc. dyn. de mém. pour un tableau d'entiersfor(int i=0, psav=pt ; i<10 ; i++, pt++)

*pt=0 ;. . .delete[] psav; // libération de la mémoire associée à ce tableau

148

148

ALLOCATION DYNAMIQUE DE MEMOIRE

allocation d'un objet:Counter *p1=new Counter; // alloc. dyn. de mém. pour un objetCounter *p2=new Counter(5); // alloc. dyn. de mém. pour un objetp1->increment();p2->increment();. . .delete p1; // libération de la mémoire associée à l'objet *p1delete p2; // libération de la mémoire associée à l'objet *p2

à la différence de la fonction malloc ou calloc, l'opérateur new fait appel au constructeur pour initialiser l'objet allouél'opérateur delete fait quant à lui appel au destructeur

149

149

ALLOCATION DYNAMIQUE DE MEMOIRE

allocation d'un tableau d'objets:Counter *psav;Counter *pc=new Counter[10]; // alloc. dyn. de mém. pour un tableau

// d'objetsfor(int i=0, psav=pc ; i<10 ; i++, pt++)

pt->affiche() ;. . .delete[] psav; // libération de la mémoire associée à ce tableau

l'opérateur new[] fait appel au constructeur par défaut ou à celui sans argument s'il existe pour initialiser chacun des objets du tableaul'opérateur delete[] fait appel au destructeur autant de fois que le tableau comporte d'objets à détruire

150

150

ALLOCATION DYNAMIQUE DE MEMOIRE

en cas d'échec de l'allocation, les opérateurs new et new[] peuvent selon le cas:– lancer une exception– retourner une adresse nulle– déclencher l'exécution d'une fonction utilisateur

le code généré par les compilateurs récents qui respectent la version 3.0 du C++ entraine par défaut le lancement d'une exception de type bad_alloc (Cf chapitre "Exceptions")il est néanmoins possible d'intervenir dans ce comportement de sorte que les opérateurs new et new[] retournent une adresse nulle ou déclenchent l'exécution d'une fonction spécifique

151

151

ALLOCATION DYNAMIQUE DE MEMOIRE

il suffit de faire appel à une fonction set_new_handler(void*), en lui passant comme argument l'adresse d'une fonction de gestion d'erreur d'allocationvoid erreur_alloc() {

cerr<<"allocation impossible!"<<endl;. . .

}int main() {

set_new_handler(erreur_alloc);for(;;) new char[10000];. . .

}

152

152

ALLOCATION DYNAMIQUE DE MEMOIRE

si l'allocation échoue, la fonction erreur_alloc est exécutée, puis une nouvelle tentative d'allocation est effectuée, et ainsi de suite.la fonction erreur_alloc doit donc tenter de libérer de la mémoire avant son retour, ou alors lancer une exception ou encore quitter l'applicationsi la fonction set_new_handler reçoit une adresse nulle, les opérateurs new et new[] renvoient une adresse nulle lorsque l'allocation échoueint main() {

set_new_handler(0);if((int* p=new int[10000])==0) {. . .}

}

153

153

ALLOCATION DYNAMIQUE DE MEMOIRE

on peut vouloir effectuer de l'allocation dynamique de mémoire au sein d'objets, ce qui revient alors à créer des objets de taille variablele destructeur doit alors libérer toute la mémoire allouée pour l'objet sous peine de consommer de la mémoirel'un ou plusieurs des attributs est de type pointeur et désigne une zone mémoire allouée dynamiquementl'exemple suivant représente une classe chaine de caractères permettant de créer des objets de taille variable, en stockant les caractères dans une zone mémoire allouée dynamiquement

154

154

ALLOCATION DYNAMIQUE DE MEMOIRE

class Chaine {char *pch;int longueur;

public:Chaine();Chaine(const char*);~Chaine();Chaine& copie(const char*);Chaine& concat(const char*);int compare(const char*) const;int length() const;void print() const;

};

155

155

ALLOCATION DYNAMIQUE DE MEMOIRE

Chaine::Chaine() {longueur=0;pch=new char[1];*pch='\0';

}Chaine::Chaine(const char* s) {

longueur=strlen(s);pch=new char[longueur+1];strcpy(pch,s);

}Chaine::~Chaine() {

delete[] pch;}

156

156

ALLOCATION DYNAMIQUE DE MEMOIRE

int Chaine::compare(const char* s) const {return strcmp(pch,s);

}int Chaine::length() const {

return longueur;}void Chaine::print() const {

cout<<pch<<endl;}

157

157

EXERCICE

Exo16: définir quelques méthodes de la classe Chaine

158

CONSTRUCTEUR DE COPIE

159

159

CONSTRUCTEUR DE COPIE

lorsque l'un des attributs d'un objet est une référence ou un pointeur, la création d'un objet à partir d'un autre de la même classe peut présenter quelque problème:

Chaine c1(c2);

en effet, en l'absence d'un constructeur adéquat, dit constructeur de copie, la création d'un objet à partir d'un autre de la même classe est malgré tout effectuéecependant, le code généré par défaut effectue une copie membre à membre des attributs: les objets c1 et c2 se partagent donc la même zone mémoire allouée dynamiquement pour stocker les caractères que ces chaines représentent

160

160

CONSTRUCTEUR DE COPIE

les objets c1 et c2 ne sont donc pas complètement distints et une modification de l'un peut entraîner de graves dommages sur l'autrece problème peut être corrigé en introduisant un constructeur dit constructeur de copie: Chaine(const Chaine&);

Chaine::Chaine(const Chaine& c) {longueur=c.longueur;pch=new char[longueur+1];strcpy(pch,c.pch);

}

161

161

CONSTRUCTEUR DE COPIE

la création d'un objet à partir d'un autre du même type se produit dans les cas suivants:– création explicite:

X x1(x2);X x3=x4;

– transmision par valeur d'un objet à une fonction ou méthode X x();f(x); // avec …f(X)

– retour par valeur d'objetsX x=g(…); // avec X g(…)

162

162

EXERCICE

Exo17: rajouter un constructeur de copie dans la classe Chaine

163

SURCHARGE DE L'OPERATEUR =

164

164

SURCHARGE DE L'OPERATEUR =

un problème similaire se rencontre lors de l'affectation de deuxobjets de mêmes types:

Chaine c1("alpha");Chaine c2("beta");c1=c2;

en effet, en l'absence d'une méthode adéquate, dite opérateur d'affectation surchargé, la copie d'un objet à partir d'un autre de la même classe est malgré tout effectuéecependant, le code généré par défaut effectue une copie membre à membre des attributs: les objets c1 et c2 se partagent donc la même zone mémoire allouée dynamiquement pour stocker les caractères que ces chaines représentent

165

165

SURCHARGE DE L'OPERATEUR =

les objets c1 et c2 ne sont donc pas complètement distints et une modification de l'un peut entraîner de graves dommages sur l'autrece problème peut être corrigé en introduisant une surcharge de l'opérateur d'affectation: Chaine& operator=(const Chaine&);

Chaine& Chaine::operator=(const Chaine& c) {if(this!=&c) {

delete[] pch;longueur=c.longueur;pch=new char[longueur+1];strcpy(pch,c.pch);

}return *this;

}

166

166

SURCHARGE DE L'OPERATEUR =

la présence d'un constructeur de copie et d'un opérateur d'affectation surchargé dans toute classe est souhaitable, ne serait-ce que pour rassurer l'utilisateurla présence nécessaire de l'un entraîne généralement la présencenécessaire de l'autretoute classe qui comporte ces deux méthodes ainsi qu'un constructeur sans argument est qualifiée de "forme canonique orthodoxe" par Bjarne Stroustrup

167

167

EXERCICE

Exo18: rajouter l'opérateur d'affectation dans la classe Chaine

168

FONCTIONS ET CLASSES AMIES

169

169

FONCTIONS ET CLASSES AMIES

une fonction amie d'une classe est une fonction qui a accès à tous les membres, mêmes privés de tous les objets de cette classeune fonction est déclarée amie d'une classe par le mot-clé friend

class A {friend void f(A&);int a;

. . .};void f(A& x) {

x.a=0; // accès à l'attribut a privé dans A}

n'étant pas un membre de la classe, l'emplacement de sa déclaration dans la classe importe peu

170

170

FONCTIONS ET CLASSES AMIES

une fonction ne peut être déclarée amie d'une classe que si cette classe a été préalablement définie, à défaut préalablement déclarée, par exemple: class A;si une fonction doit avoir accès aux membres privés des objets de deux classes, elle peut être rendue amie de chacune d'elles

class A { class B {friend int g(A&,B&); friend int g(A&,B&);int a; int b; . . . . . .

}; };int g(A& x,B& y) {

return x.a+y.b=0; // accès aux attributs a et b privés}

171

171

FONCTIONS ET CLASSES AMIES

une méthode d'une classe peut également être amie d'une autre classeclass A { class B {

int a; friend void A::f(B&);public: int b;

void f(B&); . . .}; };void A::f(B& y) {

y.b=0; // accès à l'attribut b privé dans B}

172

172

FONCTIONS ET CLASSES AMIES

une classe peut être rendue amie d'une autre classe, donnant ainsi à la première un accès illimité aux membres de la deuxième

class B;class A {

friend class B;int a;

. . .};

173

SURCHARGE D'OPERATEURS

174

174

SURCHARGE D'OPERATEURS

la surcharge d'opérateurs consiste à redéfinir certains opérateurs du langage de sorte qu'ils puissent s'appliquer aux objets:

Counter c1(5),c2(8);Counter c3=c1+c2;

– l'opérateur + n'étant pas défini pour le type Counter, le compilateur signale une erreur

– il suffit de définir l'opérateur + dans une méthode adéquate pour rendre légale cette opération

tous les opérateurs du langage C++ peuvent être surchargés sauf::: .* . ?: sizeof

il n'est pas possible de créer de nouveaux opérateurs, de modifier la priorité d'un opérateur, ni de modifier son nombre d'opérandes

175

175

SURCHARGE D'OPERATEURS

l'opérateur d'affectation déjà été examiné précédemment:Chaine& operator=(const Chaine&);

pour obtenir de nom de la méthode correspondant à un opérateur, il suffit de faire suivre le mot operator du symbole utilisé pour l'opérateur, ce symbole étant constitué de un ou deux caractèresen ce qui concerne le type de retour, il dépend essentiellement de l'opérateur utilisé, de même pour les argumentsil est possible de trouver dans une même classe le même opérateur surchargé plus d'une fois

176

176

SURCHARGE D'OPERATEURS

un moyen simple et sûr de trouver le type de retour ainsi que les arguments est de ré-écrire l'expression:

c1=c2; // est équivalent à: c1.operator=(c2);Counter c3=c1+c2; // est équivalent à: Counter c3=c1.operator+(c2);++c3; // est équivalent à: c3.operator++();

tout opérateur unaire (s'appliquant à un seul opérande) peut être défini comme méthode n'ayant pas d'argumenttout opérateur binaire (s'appliquant à deux opérandes) peut êtredéfini comme méthode ayant un seul argumentl'exemple suivant montre quelques opérateurs surchargés pour desobjets de la classe Counter

177

177

SURCHARGE D'OPERATEURS

class Counter {int count;

public:Counter();Counter(int);. . .bool operator==(const Counter&) const;bool operator!=(const Counter&) const;Counter& operator+=(const Counter&);Counter operator+(const Counter&);Counter& operator++(); // pré-incrémentationCounter operator++(int); // post-incrémentation

};

178

178

SURCHARGE D'OPERATEURS

bool Counter::operator==(const Counter& c) const {return count==c.count;

}bool Counter::operator!=(const Counter& c) const {

return count!=c.count;}Counter& Counter::operator+=(const Counter& c) {

count+=c.count;return *this;

}Counter Counter::operator+(const Counter& c) {

Counter temp(count+c.count);return temp;

}

179

179

SURCHARGE D'OPERATEURS

Counter& Counter::operator++() { // pré-incrémentation++count;return *this;

}Counter Counter::operator++(int x) { // post-incrémentation

Counter temp=*this;++count;return temp;

}

180

180

SURCHARGE D'OPERATEURS

int main() {Counter c1(5),c2(14),c3;c1++;c3=c1+c2;Counter c4(20);if(c1==c4) cout<<"c1 et c4 ont le même état"<<endl;else cout<<"c1 et c4 ont un état différent"<<endl;c4+=5;cout<<"c4: "<<c4.lirecount()<<endl;c1=c3+6;c2=8+c4; // ERREUR à la compilation

}

181

181

EXERCICE

Exo19: définir quelques opérateurs dans la classe Chaine

182

182

SURCHARGE D'OPERATEURS

les opérations: c4+=5; et c1=c3+6; sont acceptées contre toute attenteles méthodes operator+=(int) et operator+(int) n'existant pas dans la classe Counter, le compilateur fait de son mieux et utilise le constructeur Counter(int) pour créer des objets à partir des arguments de type int afin d'être en mesure d'utiliser les méthodes operator+=(const Counter&) et operator+(const Counter&)tout constructeur avec un argument joue donc le rôle d'un opérateur de conversion du type de l'argument vers le type de la classe l'opération c2=8+c4; est refusée par le compilateur car l'expression: c2=8.operator(c4); est illégale pour lui, 8 n'étant pas un objet

183

183

SURCHARGE D'OPERATEURS

afin que la conversion des opérandes puisse s'appliquer indifféremment aux opérandes de gauche ou de droite, il faut utiliser des fonctions amies en lieu et place des méthodesclass Counter {

. . .friend Counter operator+(const Counter&,const Counter&);

};Counter operator+(const Counter& cx,const Counter& cy) {

Counter temp(cx.count+cy.count);return temp;

}

184

184

SURCHARGE D'OPERATEURS

une fonction amie qui surcharge un opérateur nécessite un argument de plus que la méthode équivalente, puisqu'elle n'est pas invoquée pour un objeten règle générale, les opérateurs binaires seront de préférence surchargés comme fonctions amies de façon à permettre une plus grande latitude à l'utilisationles opérateurs suivants sont obligatoirement surchargés dans uneméthode:

= -> () [] opérateurs de conversion

185

185

EXERCICE

Exo20: définir un opérateur en fonction amie dans la classe Chaine

186

186

SURCHARGE D'OPERATEURS

les opérateurs de conversion jouent le rôle inverse du constructeur: ils permettent de convertir un objet de la classe dans un autre type

Counter c(12);int a=c; // int a=c.operator int();

un opérateur de conversion ne possède pas de type de retour, ni d'argumentla mise-en-œuvre de ces opérateurs est délicate car elle conduit rapidement à des ambiguités lors de la compilation, le compilateur ne pouvant décider de la conversion à appliquer parmi plusieurs possibilités

187

187

SURCHARGE D'OPERATEURS

class Counter {. . .

public:Counter(int); // conversion de int vers Counteroperator int() const; // conversion de Counter vers int

};Counter::operator int() const { // opérateur de conversion Counter->int

return count;}

188

188

EXERCICE

Exo21: définir un opérateur de conversion dans la classe Chaine

189

189

SURCHARGE D'OPERATEURS

les opérateurs d'entrées/sorties sont très utiles lorsqu'il est possible de les appliquer directement sur des objetsl'objet cout de la classe ostream est utilisé pour l'affichage sur le terminall'opérateur << est abondamment surchargé dans la classe ostream de façon à permettre l'affichage de toute donnée de type primitifcet opérateur n'a pas été prévu pour afficher des objets Counter ni Chaine, et la classe ostream ne peut être modifiée

Counter c1(5);cout<<c1; // cout.operator<<(c1); ou operator(cout,c1);

il suffit de surcharger l'opérateur << comme fonction amie

190

190

SURCHARGE D'OPERATEURS

class Counter {. . .

friend ostream& operator<<(ostream&,const Counter&);};

ostream& operator<<(ostream& os, const Counter& c) {os<<c.count;return os;

}

191

191

EXERCICE

Exo22: définir, dans la classe Chaine, l'opérateur d'entrées/sorties >> permettant son utilisation avec un objet cout

192

LES CLASSES INTERNES

193

193

LES CLASSES INTERNES

une classe interne (nested class) est une classe définie à l’intérieur d’une autre classe:– soit comme membre de cette classe (inner class), au même titre que ses

attributs ou méthodes– soit à l’intérieur d’une méthode de cette classe (local class)

l'intérêt principal d'une classe interne est de limiter sa visiblité à la seule classe englobante, et aux classes dérivées de celle-ciune classe interne n'a pas d'accès privilégié aux membres de sa classe englobante et réciproquementune classe interne définie comme membre d’une classe subit les mêmes règles de visiblité que les autres membres– les spécificateurs d’accès private, protected, public s’appliquent donc à une

classe interne

194

194

LES CLASSES INTERNESclass List {

ListItem *list;ListItem *end;class ListItem {

static int MAX_ELEM;ListItem *next;

public:ListItem(int =0);. . .

};public:

List();. . .

};

195

195

LES CLASSES INTERNES

la définition des méthodes de la classe interne doit indiquer le nom complet de la méthode, qui inclut celui de la classe englobante:

int List::ListItem::MAX_ELEM=10000;

List::ListItem::ListItem(int val):next(0) {}

List::List():list(0),end(0) {}

196

196

LES CLASSES INTERNES

une classe locale est une classe définie à l'intérieur d'une méthode (ou d'une fonction): la définition de la classe est obligatoirement monolithique, ce qui limite en général sa complexitéclass A {

public:void alpha(int val) {

class B { // classe interne locale à la méthode alpha . . .};. . .

}. . .

};

197

197

EXERCICE

Exo23: mise en œuvre d'une classe interne

198

LA COMPOSITION

IMPLEMENTATION DE LA COMPOSITIONCONSTRUCTION D'UN OBJET COMPOSEAFFECTATION D'OBJETS COMPOSESDESTRUCTION D'UN OBJET COMPOSE

199

199

IMPLEMENTATION DE LA COMPOSITION

la composition traduit la relation A UN, POSSEDE, ou EST COMPOSE DEil se traduit simplement en C++ par la présence d'objets en attributclass Personne {

string nom; // objet de la classe stringstring prenom; // objet de la classe stringint age;

public:Personne();Personne(const string&, const string&, int);. . .

};

200

200

CONSTRUCTION D'UN OBJET COMPOSE

la construction d'un objet composé entraine automatiquement par défaut la construction préalable des objets attributs le composant, dans l'ordre de déclaration de ces attributs dans la classe– dans l'exemple précédent, le constructeur de nom , puis celui de prenom, puis

celui de Personne est appelé

le constructeur sans argument des objets attributs est appelé par défaut Personne::Personne() // les objets nom et prenom sont initialisés {} // avec le constructeur sans argument de string

201

201

CONSTRUCTION D'UN OBJET COMPOSE

l'appel à un autre constructeur que celui sans argument peut être indiqué dans la liste d'initialisateurs de membres:Personne::Personne(const string& n, const string&, int a)

:nom(n), prenom(p), age(a) {}

par défaut, la création d'un objet composé à partir d'un autre du même type revient à créer chacun des attributs du premier à partir des attributs correspondants du deuxième– s'il existe, le constructeur de copie des objets attributs sera appelé– sinon, les objets attributs seront recopiés membre à membrePersonne martin("MARTIN","Pierre",30);Personne m=martin; // les attributs nom, prenom de l'objet m sont crées à partir

// des objets nom et prenom de martin

202

202

AFFECTATION D' OBJETS COMPOSES

l'affectation d'un objet composé à un autre du même type revientégalement à affecter chacun des attributs du premier aux attributs correspondants du deuxième– s'il existe, l'opérateur d'affectation surchargé des objets attributs sera appelé– sinon, les objets attributs seront recopiés membre à membrePersonne martin("MARTIN","Pierre",30);Personne p;p=martin; // les attributs nom, prenom de l'objet p sont initialisés par

// les objets nom et prenom de martin

203

203

DESTRUCTION D'UN OBJET COMPOSE

la destruction d'un objet composé s'effectue dans l'ordre inverse de sa création: le destructeur de l'objet composé est appelé avant ceux des objets attributs le constituant – dans l'exemple précédent, le destructeur de Personne, puis celui de prenom,

puis celui de nom est appelé (s'ils existent)class Personne {

string nom; // objet de la classe stringstring prenom; // objet de la classe stringint age;

public:. . .

};

204

204

EXERCICE

Exo24: réalisation d'une classe Personne

205

L'HERITAGEIMPLEMENTATION DE L'HERITAGELA CONSTRUCTION D'UN OBJET DERIVEL'APPEL DES METHODESHERITAGES PUBLIC, PRIVE ET PROTEGEHERITAGE PUBLICHERITAGE PROTEGEHERITAGE PRIVEL'ACCES "PROTECTED"DESTRUCTION D'UN OBJET DERIVELA MANIPULATION D'UN OBJET DERIVEL'HERITAGE MULTIPLEL'HERITAGE MULTIPLE REPETE

206

206

IMPLEMENTATION DE L'HERITAGE

l'héritage traduit la relation "EST-UN"l'héritage consiste à faire bénéficier une classe (les objets de cette classe) des attributs et des méthodes d'une autre classeC++ supporte l'héritage multiple

207

207

IMPLEMENTATION DE L'HERITAGE

l'héritage s'exprime par l'opérateur : suivi du type de l'héritage, à savoir public, protected ou private

class D : public B { . .

};la classe D est la sous-classe (ou classe dérivée ou classe fille) de B la classe B est la super-classe (ou classe de base ou classe parente) de Dplusieurs classes filles peuvent hériter d'une même classe parente

208

208

IMPLEMENTATION DE L'HERITAGE

l'héritage public est le plus largement utilisé car il correspond aux besoins les plus courants

class D : public B { . .

};en l'absence d'un spécificateur, l'héritage est privé et son usage beaucoup plus rare

class E : A { . .

};

209

209

IMPLEMENTATION DE L'HERITAGE

l'héritage est transitif et conduit à une hiérarchie d'héritageune sous-classe peut ainsi avoir plusieurs super-classes, au traversdes relations d'héritage qui peuvent exister entre ces dernièresen plus des méthodes publiques de sa classe, il est possibled'invoquer les méthodes publiques de sa(ses) super-classe(s)un objet d’une sous-classe est en général "plus gros" qu’un objet desa super-classe, car en plus des attributs définis dans sa propre classe, il comporte ceux de sa(ses) super-classe(s)il a de plus un comportement plus varié qu'un objet de sa super-classe, puisqu’il dispose de toutes les méthodes héritées, en plus decelles qu’il possède dans sa propre classe

210

210

LA CONSTRUCTION D'UN OBJET DERIVE

l'initialisation des attributs d'un objet dérivé s'effectue en faisant appel aux constructeurs, lesquels appellent à leur tour ceux de laclasse de base soit de manière implicite, soit explicitementlors de la construction d'un objet dérivé, les constructeurs des classes de base sont d'abord appelés, dans l'ordre de la hiérarchie de classe, puis ceux des attributs de l'objet dérivé et enfin celui de l'objet dérivétout objet dérivé voit ses attributs correspondant à sa classe de base initialisés implicitement par le constructeur sans argument de sa classe de basesi l'on souhaite faire appel à un autre constructeur que celui sans argument, il faut l'indiquer dans la liste d'initialisateurs de membres

211

211

LA CONSTRUCTION D'UN OBJET DERIVE

class Personne {char nom[20];public:

Personne() {nom[0] = '\0'; }Personne (const char* n) {strcpy( nom,n); }

};class Employe:public Personne {

double salaire;public:

Employe() {salaire=0.0;} // appel implicite du constructeur Personne()Employe (const char* n, double s):Personne(n) // appel explicite au constructeur

// Personne(const char*){ salaire = s; }

};

212

212

L'APPEL DES METHODES

lorsqu'une méthode est invoquée pour un objet, le compilateur recherche d'abord cette méthode dans la classe de cet objetsi aucune méthode de ce nom n'est présente dans la classe, il remonte dans la hiérarchie de classes pour continuer sa recherchetoutes les méthodes publiques des classes de base peuvent ainsi être invoquées pour un objet d'une classe dérivée, en plus des méthodes publiques propres à cette classe

213

213

L'APPEL DES METHODESclass Personne {

char nom[20];public:

. . .void affiche_pers () {cout<<"nom : " << nom <<endl; }

};class Employe:public Personne {

double salaire;public:

Employe (const char* n, double s) :Personne(n),salaire(s){}};int main () {

Employe martin=new Employe ("MARTIN", 15000);martin.affiche_pers (); // méthode héritée de la classe Personne

}

214

214

L'APPEL DES METHODES

si l'on souhaite compléter le code d'une méthode héritée, il est avantageux de définir une méthode qui appelle la méthode héritée:

class Personne {char nom[20];public:

Personne (const char* n) { strcpy(nom,n); }void affiche_pers () { cout<<"nom : " << nom <<endl;}

};class Employe:public Personne {

double salaire;public:

Employe (const char* n, double s):Personne(n),salaire(s) {}void affiche_emp () {

affiche_pers (); // appel d'une méthode héritéecout<<"salaire : "<< salaire<<endl;

}};

215

215

L'APPEL DES METHODES

si l'on souhaite conserver le même nom qu'une méthode de la super-classe, tout en faisant appel à cette dernière dans la sous-classe, il faut indiquer au compilateur qu'il ne s'agit pas d'une méthode récursive en faisant précéder l'appel à la méthode du nom de sa classe suivi de l'opérateur ::

Personne::affiche()

plus généralement, cette notation est utilisée lorsque l'on souhaite simplement préciser la classe dont fait partie la méthode appelée

216

216

L'APPEL DES METHODESclass Personne {

char nom[20];public:

Personne (const char* n) { strcpy(nom,n); }void affiche () { cout<<"nom : " <<nom<<endl;}

};class Employe:public Personne {

double salaire;public:

Employe (const char* n, double s):Personne(n),salaire(s) {}void affiche () {

Personne::affiche(); // appel d'une méthode héritée de même nomcout<<"salaire : "<<salaire<<endl;

}};

217

217

EXERCICE

Exo25: réalisation d'une classe Employe sous-classe de Personne

218

218

HERITAGES PUBLIC, PRIVE ET PROTEGE

l'héritage traduit le bénéfice, pour la classe dérivée, de tous les membres de sa classe parentel'accès aux membres hérités n'est pas pour autant garanti, car il suit les règles de l'encapsulation, et dépend également du type de l'héritage mis-en-œuvretrois mode d'héritage sont proposés en C++:public protégé privé

class B : public A { class C:protected A { class D: private A {// class D:A { . . . . . .

}; }; };

219

219

HERITAGES PUBLIC, PRIVE ET PROTEGE

le mode d'héritage se combine avec le spécificateur d'accès des membres hérités pour donner accès ou non à ces derniers:

inaccessibleinaccessibleinaccessibleprivate

privateprotectedprotectedprotected

privateprotectedpublicpublic

privateprotectedpublicmode accès

220

220

HERITAGES PUBLIC, PRIVE ET PROTEGE

soit la classe suivante:class A {

private:int x;void f();

protected:int y;void g();

public:int z;void h();

};

221

221

HERITAGE PUBLIC

héritage public: le plus fréquemment rencontréles membres hérités conservent leur niveau d'encapsulation dans la classe dérivéeclass B: public A {

public:void f() {

// x=1; // inaccessible y=2; // ok, y est dorénavant protected (ce qu'il était déjà)z=3; // ok, z est dorénavant public (ce qu'il était déjà)

}};

222

222

HERITAGE PUBLIC

int main() {C cc;// cc.f(); // inaccessible// cc.g(); // inaccesible car g() est protectedcc.h(); // ok car h() est public

}

223

223

HERITAGE PROTEGE

héritage protégé: rarement rencontréles membres hérités public et protected dans la classe de base deviennent protected dans la classe dérivée class C: protected A {

public:void f() {

// x=1; // inaccessible y=2; // ok, y est dorénavant protected (ce qu'il était déjà)z=3; // ok, z est dorénavant protected (il était public)

}};

224

224

HERITAGE PROTEGE

int main() {C cc;// cc.f(); // inaccessible// cc.g(); // inaccessible car g() est dorénavant protected// cc.h(); // inaccessible car h() est dorénavant protected

}

225

225

HERITAGE PRIVE

héritage privé: rarement rencontréles membres hérités public et protected dans la classe de base deviennent private dans la classe dérivée class D: private A { // ou class D:A {

public:void f() {

// x=1; // inaccessible y=2; // ok, y est dorénavant private (il était protected)z=3; // ok, z est dorénavant private (il était public)

}};

226

226

HERITAGE PRIVE

int main() {C cc;// cc.f(); // inaccessible car f() est private// cc.g(); // inaccessible car g() est dorénavant private// cc.h(); // inaccessible car h() est dorénavant private

}

227

227

L'ACCES "PROTECTED"

si l'on souhaite encapsuler les données, il est souhaitable de lesrendre privées dans la classedans ce cas, une méthode d'une sous-classe n'a pas directement accèsaux attributs définis dans sa super-classe: elle doit passer par desméthodes publiques de sa super-classe (en supposant qu'ellesexistent)l'encapsulation peut être conservée, tout en autorisant l'accès auxdonnées depuis les méthodes des sous-classes , en déclarant lesdonnées protected dans la super-classe– ces données restent privées pour un objet de la super-classe– les méthodes peuvent également être déclarées en protected

228

228

L'ACCES "PROTECTED"class Employe:public Personne {

protected:double salaire;

public:Employe (const char* n, double s):Personne(n), salaire(s){}void setSalaire (int s) {salaire = s;}

};int main () {

Employe Lucien ("Lucien", 17000);Lucien.salaire = 20000; // ERREUR ! salaire est protectedLucien.setSalaire (20000); // OK

}

229

229

L'ACCES "PROTECTED"class Technicien:public Employe {

protected:char specialite[20];int echelon;

public:Technicien (const char* n, double s, const char* sp, int e):Employe(n,s) {

specialite = sp; echelon = e;

}void fixer_salaire (int val) {

salaire = val; // accès à un attribut protected dans la classe Employe}

};

230

230

EXERCICE

Exo26: mise en place d'un membre protected dans la classe Employe

231

231

DESTRUCTION D'UN OBJET DERIVE

la destruction d'un objet dérivé s'effectue dans l'ordre inverse de sa création: le destructeur de l'objet dérivé est appelé avant ceux des attributs et avant ceux des classes de basela destruction d'un objet Technicien entrainera l'appel aux destructeur de Technicien, puis à celui de Employe et enfin à celui de Personne

232

232

LA MANIPULATION D'UN OBJET DERIVE

un objet d'une classe dérivée peut être implicitement converti enobjet d'une classe de base dans la hiérarchie:

Employe martin ("MARTIN", 15000);Personne durand("DURAND");Personne m = martin; // l'objet martin est une PersonneEmploye d = durand; // ERREUR! l'objet durand n'est pas un Employe

il est donc possible de passer en paramètre à une méthode un objetqui sera reçu dans un argument du type d'une de ses classes de base

int main () {Employe martin("MARTIN", 15000);majus(martin);

}void majus(Personne p) {. . .}

233

233

LA MANIPULATION D'UN OBJET DERIVE

en pratique, il est plus courant et beaucoup plus utile de disposer de pointeurs ou de références sur les objets dérivés pour les manipuler

Employe martin ("MARTIN", 15000);Personne *p = &martin; // l'objet martin est une PersonnePersonne& rm=martin; // idemPersonne durand("DURAND");Employe *p = &durand; // ERREUR! l'objet durand n'est pas un EmployeEmploye & rd=durand; // idem

234

234

LA MANIPULATION D'UN OBJET DERIVE

l'héritage autorise donc la manipulation d'objets dérivés en lesconsidérant comme des objets de leur classe de base

int main () {Personne durand("DURAND");Employe martin("MARTIN", 15000);durand.affiche_pers (); // nom : DURANDmartin.affiche_emp (); // nom : MARTIN

// salaire : 15000Personne *p = &martin; // l'objet martin est une Personnep->affiche_pers (); // nom : MARTIN

. . .}

235

235

LA MANIPULATION D'UN OBJET DERIVE

manipuler un objet dérivé via un pointeur ou une référence du type de sa classe de base revient à réduire son comportement à celui que fournit sa classe de base – un objet Employe manipulé via un pointeur ou une référence de type Personne

se comporte comme une Personne

un objet dérivé peut nécessiter d’être manipulé via un pointeur de sa propre classe, de façon à pouvoir utiliser les méthodes (nonpolymorphes) de sa classe– comment faire si l'on veut fixer le salaire de cet Employe alors qu'on ne

dispose que d'un pointeur de type Personne sur cet objet ?

un transtypage est alors nécessaire pour y parvenir

236

236

LA MANIPULATION D'UN OBJET DERIVE

int main () {Employe duval ("DUVAL", 30000);

f(&duval);}void f(Personne *p)

Employe *pe=(Employe*)p; . .p->fixer_salaire(5);

}

237

237

EXERCICE

Exo27: création d'un tableau d'objets recevant des objets des classes Personne, Employe et Technicien

238

238

L'HERITAGE MULTIPLE

l'héritage multiple consiste à faire hériter une classe de plusieurs classe parentes: il suffit d'indiquer la liste des classes parentes, séparées les unes des autres par une virgule

class A {. . .};class B {. . .};class C : public A, public B {. . .};

Toute représentation ou reproduction intégrale ou partielle faite sans le consentement de l'auteur Denis MADEC ou de ses ayants droits ou ayant cause est illicite selon le code de la propriété intellectuelle du 1er juillet 1992 et constitue une contefaçon réprimée par le code pénal.

239

239

L'HERITAGE MULTIPLEclass Bateau {

float tonnage;float tirant;float longueur;

public:Bateau();Bateau(float,float,float);float getTonnage() const;float getTirant() const;float getLongueur() const;void setTonnage(float);void setTirant(float);void setLongueur(float);void affiche() const;

};

240

240

L'HERITAGE MULTIPLEclass Avion {

float charge_alaire;float envergure;int vitesse_min;

public:Avion();Avion(float,float,int); float getCharge_alaire() const;float getEnvergure() const;int getVitesse_min() const;void setCharge_alaire(float);void setEnvergure(float);void setVitesse_min(int);void affiche() const;

};

241

241

L'HERITAGE MULTIPLE

Un Hydravion EST-UN Avion ET un Bateau:

class Hydravion: public class Avion, public class Bateau {float capacite;

public:Hydravion();Hydravion(float,float,int,float,float,float,float);float getCapacite() const;void setCapacite(float);void affiche() const;

};

242

242

L'HERITAGE MULTIPLE

Hydravion::Hydravion(): capacite(0.0){}Hydravion::Hydravion(float chg,float env, int vit ,float ton,

float tir,float lg,float cap): Avion(chg,env,vit),Bateau(ton,tir,lg),capacite(0.0)

{}float Hydravion::getCapacite() const {return capacite;}void Hydravion::setCapacite(float cp) {capacite=cp;}void Hydravion::affiche() const {

Avion::affiche();Bateau.affiche();cout<<"Capacite: "<<capacite<<endl;

}

243

243

L'HERITAGE MULTIPLEREPETE

en supposant maintenant qu'une classe Vehicule existe:class Vehicule {

int puissance;int vitesse_max;

public:Vehicule ();Vehicule (int,float);int getPuissance() const;int getVitesse_max() const;void setPuissance(int);void setVitesse_max(int);void affiche() const;

};

244

244

L'HERITAGE MULTIPLEREPETE

il devient naturel d'établir les relations d'héritage entre Avion et Vehicule d'une part, entre Bateau et Vehicule d'autre part: class Avion:public Vehicule {. . .};class Bateau:public Vehicule {. . .};class Hydravion: public class Avion, public class Bateau {. . .};

245

245

L'HERITAGE MULTIPLEREPETE

class Bateau: public Vehicule {float tonnage;float tirant;float longueur;

public:Bateau();Bateau(int,float,float,float,float);float getTonnage() const;float getTirant() const;float getLongueur() const;void setTonnage(float);void setTirant(float);void setLongueur(float);void affiche() const;

};

246

246

L'HERITAGE MULTIPLEREPETE

class Avion: public Vehicule {float charge_alaire;float envergure;int vitesse_min;

public:Avion();Avion(int,float,float,float,int); float getCharge_alaire() const;float getEnvergure() const;int getVitesse_min() const;void setCharge_alaire(float);void setEnvergure(float);void setVitesse_min(int);void affiche() const;

};

247

247

L'HERITAGE MULTIPLEREPETE

une instance d'Hydravion comporte alors les attributs de Vehicule en double, puisqu'elle en hérite via la classe Avion mais aussi par la classe Bateaula modification de la puissance d'un objet Hydravion s'appliquera seulement à l'un des deux attributs puissance, l'autre conservant sa valeur, aucun mécanisme n'assurant la mise en phase des élémentsdu doublonle C++ permet de traiter ce cas particulier par utilisation de classes de base virtuelles

248

248

L'HERITAGE MULTIPLEREPETE

La classe Vehicule devient une classe de base virtuelle: une seule instance de Vehicule apparaitra dans un objet Hydravionclass Avion: public virtual Vehicule {. . .};class Bateau: public virtual Vehicule {. . .};class Hydravion: public class Avion, public class Bateau {. . .};

249

249

L'HERITAGE MULTIPLEREPETE

l'instance unique de Vehicule qui apparaît désormais dans Hydravion doit par contre être initialisée explicitement par la classe la plus dérivée, soit Hydravionen effet, les constructeurs de Avion et Bateau n'initialiseront pas l'instance Vehicule de l'objet Hydravion

250

250

L'HERITAGE MULTIPLEREPETE

Hydravion::Hydravion(): capacite(0.0) // appels implicites aux constructeurs sans argument:// Avion(), Bateau(),Vehicule(){}

Hydravion::Hydravion(int puis, int vmax,float chg,float env, int vmin,float ton,float tir,float lg,float cap):

Avion(puiss,vmax,chg,env,vit),Bateau(puiss,vmax,ton,tir,lg),Vehicule(puiss,vit),capacite(0.0)

{}

251

251

EXERCICE

Exo28: mise en place d'un héritage multiple répété

252

LE POLYMORPHISME

INTERET DU POLYMORPHISMELES METHODES VIRTUELLESLES DESTRUCTEURS VIRTUELSLES METHODES ABSTRAITESLES CLASSES ABSTRAITES

253

253

INTERET DU POLYMORPHISME

le polymorphisme consiste à manipuler des objets sans se soucier de leur classe (faisant néanmoins partie d'une hiérarchie de classes)l'héritage autorise la manipulation d'objets dérivés en les considérant comme des objets de leur classe de base (relation "EST-UN"),cependant ils perdent alors leur comportement d'objet dérivé si l'on souhaite manipuler des objets dérivés en les considérant comme des objets de leur classe de base, tout en bénéficiant desspécificités qui leurs sont rattachées, il faut définir des méthodes polymorphes dites aussi "virtuelles"des méthodes polymorphes ou virtuelles sont des méthodes appartenant à des classes liées par héritage et qui ont le mêmeprototype, comportant de plus le préfixe virtual

254

254

LES METHODES VIRTUELLES

class Personne {char nom[20];public:

virtual void affiche () { // méthode virtuellecout<<"nom : " <<nom<<endl;

}};class Employe:public Personne {

double salaire;public:

void affiche() { // méthode virtuelle (le mot-clé virtual n'est pas nécessaire)Personne::affiche();cout<<"salaire : "<<salaire <<endl;

}};

255

255

LES METHODES VIRTUELLES

int main () {Employe martin("MARTIN", 15000);Personne *p=martin; // l'objet martin est une Personnep->affiche(); // nom : MARTIN

// salaire : 15000

Personne& rp = martin; // l'objet martin est une Personnerp.affiche(); // nom : MARTIN

// salaire : 15000. . .

}

256

256

LES METHODES VIRTUELLES

les méthodes polymorphes peuvent ne pas être redéfinies dans les classes dérivées. Dans ce cas, ce sera la méthode polymorphe de laclasse de base immédiatement au-dessus qui sera utiliséepour mettre en oeuvre le polymorphisme, la méthode polymorphe doit être soit héritée, soit déclarée dans la classe de base au traversde laquelle est manipulé l'objet dérivéune méthode virtuelle dans les classes dérivées ne peut avoir unaccès plus restrictif que la méthodes d'origine: si cette dernière est publique, la méthode virtuelle dans une classe dérivée doit aussi être publique

257

257

LES METHODES VIRTUELLES

class A {public:

virtual int display() { ... }virtual void affiche() { ... }

};class B:public A {

public:virtual char* getchaine() { ... }virtual void affiche() { ... }

};class C:public B {

public:virtual char* getchaine() { ... }virtual int display() { ... }

};

258

258

LES METHODES VIRTUELLES

int main() {B b;B *pb=&b;A *pa=pb;C c;B *ppb=&c;A *ppa=ppb;pa->affiche(); // affiche() de B : polymorphismepb->affiche(); // affiche() de Bpa->display(); // display() de Apb->display(); // display() de A : héritageppa->affiche(); // affiche() de B : polymorphismeppa->display(); // display() de C : polymorphismeppa->getchaine(); // ERREUR! getchaine() n'existe pas dans Appb->getchaine(); // getchaine() de C : polymorphismeppb->display(); // display() de C : polymorphisme

}

259

259

LES DESTRUCTEURS VIRTUELS

lorsque des objets sont manipulés via des pointeurs ou des références du type d'une classe de base, la destruction des objets manipulés peut présenter quelques difficultés

class A{public:

. . .~A() {…} // destructeur

};class B:public A {

public:. . .~B() {…} // destructeur

};

260

260

LES DESTRUCTEURS VIRTUELS

int main(){A *pa=new B;. . .delete pa; // seul de destructeur de A est appelé

}

il est alors pudent de rendre virtuels les destructeurs de façon à ce que le destructeur de l'objet réellement manipulé soit égalementappeléseul le destructeur de la classe de base nécessite la présence du mot-clé virtualafin de faciliter la compréhension des programmes et garantir leur fonctionnement, il est souhaitable de placer virtual devant tous les destructeurs

261

261

LES DESTRUCTEURS VIRTUELS

class A{public:

. . .virtual ~A() {…} // destructeur vituel

};class B:public A {

public:. . .~B() {…} // destructeur virtuel

};

262

262

EXERCICE

Exo29: mise en place d'une méthode virtuelle dans les classes Personne, Employe, Actionnaire, Directeur

263

263

LES METHODES ABSTRAITES

les méthodes abstraites sont des méthodes sans définition qui doiventpar conséquent être définies dans les sous-classesen C++, les méthodes abstraites sont mises en œuvre par des méthodes virtuelles puresclass Maclasse {

. .virtual void f()=0; // méthode virtuelle pure (méthode sans définition)

};une classe dont au moins une méthode est virtuelle pure est nécéssairement une classe abstraitetoutes les méthodes virtuelles pures doivent être définies dans lessous-classes sous-peine d'obtenir des sous-classes abstraites.

264

264

LES CLASSES ABSTRAITES

une classe est abstraite si elle comporte au moins une méthode virtuelle pureune classe abstraite est une classe non instanciableune classe abstraite a uniquement pour rôle de généraliser d'autresclasses en devenant ainsi leur classe parenteune classe abstraite correspond en général à un concept tout enn'étant pas nécessairement justifié dans le cadre de la modélisation du monde réelclass Component { // il s’agit de modéliser un composant

// graphique. .

};

265

265

LES CLASSES ABSTRAITES

l’intérêt est de factoriser les caractéristiques (attributs) etcomportement (méthodes) d’objets différentss’il n’est pas possible de créer des objets d’une classe abstraite, il esten revanche possible de définir des pointeurs ou des références surdes objets des sous-classes

Component c; // ERREUR !Button b;Component *cx=&b; // OK, car Button hérite de Component

266

266

EXERCICE

Exo30: définition d'une classe abstraite comportant une méthode virtuelle pure

267

IDENTIFICATION DE TYPE A L'EXECUTION (RTTI)

Toute représentation ou reproduction intégrale ou partielle faite sans le consentement de l'auteur Denis MADEC ou de ses ayants droits ou ayant cause est illicite selon le code de la propriété intellectuelle du 1er juillet 1992 et constitue une contefaçon réprimée par le code pénal.

268

268

IDENTIFICATION DE TYPE A L'EXECUTION (RTTI)

lorsque des objets sont manipulés via des références ou des pointeurs du type de leur classe de base, une partie seulement de leurs méthodes peut seule être utiliséeil est parfois nécessaire de pouvoir manipuler ces mêmes objets via une référence ou un pointeur du type de leur propre classe: une conversion explicite sera alors nécessaire, en utilisant dynamic_castdynamic_cast vérifie en effet que la conversion est légitime, sinon renvoie la valeur 0 s'il s'agit d'une conversion entre pointeurs, et lance une exception (Cf chapitre "Exceptions") de type bad_cast s'il s'agit d'une conversion entre références

269

269

IDENTIFICATION DE TYPE A L'EXECUTION (RTTI)

la fonction suivant reçoit en argument un tableau dans lequel setrouvent des objets Avion, Bateau ou Hydravionl'instruction dynamic_cast<Hydravion*>(t); essaie d'effectuer une conversion du pointeur t de type Vehicule* vers le pointeur hp de type Hydravion*void getCapaciteFlotte(Vehicule *t, int n) {

float capacite=0.0f;Hydravion *hp;for(int i=0;i<n;i++,t++) {

hp=dynamic_cast<Hydravion*>(t);if(hp!=NULL) capacite+=hp->getCapacite();

}}

270

270

IDENTIFICATION DE TYPE A L'EXECUTION (RTTI)

dans certains cas, il nécessaire de connaître précisément le type d'un objet manipulé via un pointeur ou une référence d'un type d'une classe de baseon parle alors de RunTime Type Information (RTTI)dynamic_cast utilise ce mécanisme pour son fonctionnementl'opérateur typeid va plus loin et renvoie un objet de la classe type_info permettant d'obtenir le nom du type d'un objet ou d'effectuer des comparaisonssyntaxe:

typeid(objet)typeid(type)

271

271

IDENTIFICATION DE TYPE A L'EXECUTION (RTTI)

l'exemple précédent peut être remodelé de façon à utiliser typeid

void getCapaciteFlotte(Vehicule *t, int n) {float capacite=0.0f;Hydravion *hp;for(int i=0;i<n;i++,t++) {

if(typeid(*t)==typeid(Hydravion)) {hp=dynamic_cast<Hydravion*>(t);capacite+=hp->getCapacite();

}}

}

272

272

IDENTIFICATION DE TYPE A L'EXECUTION (RTTI)

les objets type_info peuvent être comparés grâce aux opérateurs == et != surchargésle nom du type d'un objet peut également être obtenu grâce à la méthode name de la classe type_info qui renvoie un pointeur char* sur le nom du type– Si p est un pointeur sur un objet, le type de cet objet apparaît par l'instruction:

cout<<"type: "<<typeid(*p).name()<<endl;

l'utilisation de type_info suppose la présence de la directive: #include <typeinfo>

273

273

EXERCICE

Exo31: mise en œuvre de l'opérateur typeid sur les objets du tableau tev

274

LES EXCEPTIONS

275

275

LES EXCEPTIONS

les anomalies survenant lors de l'exécution d'un programme C++ peuvent être traités par un mécanisme d'exceptions ce mécanisme est contrôlé par les mots-clés try, catch, throwle mot-clé try permet de définir un bloc d'instructions susceptible de lancer une ou plusieurs exceptionssuivant immédiatement le bloc try, le mot-clé catch, suivi d'un argument, permet de capturer une exception d'un certain type il peut y avoir plusieurs catch successifs après un même bloc try. Ceci permet de capturer des exceptions différentes pouvant se produire dans les instructions du bloc try

276

276

LES EXCEPTIONS

try {Chaine c1("Bonjour");// lance un objet de la classe bad_alloc sic1.concat("à tous!"); // l'allocation dynamique ne peut avoir lieuchar c=c1[20]; // lance un objet de la classe Debordement

. . .}catch(bad_alloc){

cerr<<"problème d'allocation!"<<endl;exit(1);

}catch(Debordement d) {cerr<<"debordement d'indice: "<<d.getIndice()<<endl;exit(1);

}

277

277

LES EXCEPTIONSclass Chaine {

char *pch;int longueur;

public:Chaine();Chaine(const char*);~Chaine();Chaine& copie(const char*);Chaine& concat(const char*);int compare(const char*) const;char& operator[](int) const;int length() const;void print() const;

};

278

278

LES EXCEPTIONS

class Debordement {int indice;

public:Debordement(){indice=0;}Debordement(int i){indice=i;}int getIndice() const {

return indice;}

};char& Chaine::operator[](int) const {

if(i<0 || i>longueur) thow Debordement(i); // lance un objet exceptionreturn *(pch+i);

}

279

279

LES EXCEPTIONS

lorsque l'on souhaite capturer toute exception, sans se préoccuper de son type, on peut utiliser catch(…)

try {. . .

}catch(…) {cerr<<"exception inattendue!"<<endl;exit(1);

}

280

280

EXERCICE

Exo32: mise en place d'une gestion d'exception dans la classe Chaine

281

LA GENERICITE

TEMPLATE DE FONCTIONTEMPLATE DE CLASSE

282

282

TEMPLATE DE FONCTION

lorsqu'une fonction doit être surchargée, il faut la définir autant de fois que nécessaire, parfois en ne modifiant que légèrement le codeles patrons (template) de fonctions permettent de ne définir la fonction qu'une seule fois, le type des arguments étant paramétrétout template de fonction doit être précédé du mot-clé template suivi de <class identificateur>, identificateur représentant le type qu'il faudra préciser à l'appel

template <class Type> Type max(Type x, Type y) {return x>y?x:y;

}

283

283

TEMPLATE DE FONCTION

int main() {int a=5, b=7,c;double d=78.90, e=564.89, f;Counter c1(45), c2(876),c3;

c=max(a,b);cout<<"max de a, b: "<<c<<endl;f=max(d,e);cout<<"max de d, e: "<<f<<endl;c3=max(c1,c2);cout<<"max de c1, c2: "<<c3<<endl;. . .

}

284

284

TEMPLATE DE FONCTION

le compilateur substitue le paramètre Type du template de fonction par le type correspondant à l'appelle code de la fonction template doit être accessible au compilateur lorsqu'il rencontre l'appel à cette fonction: ce code est en général par commodité placé dans un fichier d'entête .hplusieurs paramètres peuvent être précisés dans un template

template <class T1, class T2> T1 fonc(T1 x, T2 y) {. . .

}

285

285

TEMPLATE DE CLASSE

comme les patrons de fonctions, les patrons de classe permettent de définir une classe une seule fois, cette classe étant prévue pour opérer sur un ou des types de données paramétrésl'exemple suivant montre un patron de classe destiné à créer des piles d'objetsdans leur définition, le nom des méthodes est précédé de Stack<T>:: et chaque méthode est également précédée de template <class T>lors de la création d'une instance d'un patron de classe, il faut préciser explicitement le type utilisé, pour permettre au compilateur de générer de code des méthodes

Stack<int> s1(50);Stack<Counter> s2;

286

286

TEMPLATE DE CLASSE

template <class T> class Stack {enum limits{EMPTY=-1, MAX_DEFAUT=100};const int MAX_SIZE;T* p;int top;

public:Stack();Stack(int);~Stack();bool isempty() const;T get() const;Stack& put(T);

};

287

287

TEMPLATE DE CLASSE

template <class T> Stack<T>::Stack(): MAX_SIZE(MAX_DEFAUT), p(new T[MAX_DEFAUT]), top(EMPTY)

{}template <class T> Stack<T>::Stack(int taille):MAX_SIZE(taille),

p(new T[taille]), top(EMPTY) {}template <class T> Stack<T>::~Stack() {

delete[] p;}template <class T> bool Stack<T>::isempty() const {

return (top==EMPTY)?true:false;}

288

288

TEMPLATE DE CLASSE

template <class T> T Stack<T>::get() const {if(top==EMPTY) throw StackEmpty();return p[top--];

}template <class T> Stack& Stack<T>::put(T x) {

if(top<MAX_SIZE) p[++top]=x;else throw StackFull();return *this;

}

289

289

TEMPLATE DE CLASSE

int main() {Stack<int> s1(50);Stack<Counter> s2;for(int i=0;i<10;i++) {

s1.put(2*i);}Counter c1,c2(5),c3(456),c4(765);s2.put(c1).put(c2).put(c3).put(c4);while(!s1.isempty()) cout<<s1.get()<<endl;while(!s2.isempty()) cout<<s2.get()<<endl;

. . .}

290

290

EXERCICE

Exo33: réalisation d'un template de classe

291

LA BIBIOTHEQUE STL

PRESENTATIONENTREES/SORTIESCHAINES DE CARACTERES

292

292

PRESENTATION

une des lacunes majeures du C++ a été, dès le départ, l'absence de réelle bibliothèque associée au langage (hormis celle du C et quelques classes d'entrées/sorties)les éditeurs ont donc vendu leur compilateur C++ avec une bibliothèque généralement bien garnie, liée à leur plateforme dedéveloppementaucune compatilité n'a été envisagée entre les bibliothèques desdifférents éditeurscette lacune a été partiellement comblée avec l'apparition progressive de la Standard Template Library (STL)plusieurs versions de la STL ont vu le jour, la dernière étant définie dans l'espace de noms std

293

293

PRESENTATION

la STL est une petite bibliothèque standard qui offre au développeur des classes et fonctions template utilitaires en plus des chaines de caractères et des entrées/sorties, cette bibliothèque est en fait composée de 5 catégories principales d'objets:– les algorithmes– les conteneurs– les itérateurs– les fonctions– les adaptateurs

294

294

PRESENTATION

sur de nombreux compilateurs cohabitent encore l'ancienne et la nouvelle bibliothèquel'ancienne bibliothèque n'utilise pas les espaces de noms et il suffit d'inclure les fichiers d'entête .h adéquats pour compiler

#include <iostream.h>

la nouvelle bibliothèque définit toutes ses classes et fonctions dans l'espace de noms std et utilise des fichiers d'entête sans extension .h

#include <iostream>using namespace std;

il est donc possible d'utiliser les classes des deux bibliothèques dans un même programme

295

295

PRESENTATION

afin de faciliter la cohabitation avec les programmes C, la bibliothèque du C intégrée avec les compilateurs C++ possède également une version équivalente dans l'espace de noms std: ses fichiers d'entête reprennent le même nom que ceux du C, sans l'extension .h et avec la lettre c comme préfixe

#include <cstdio>#include <cstring>

296

296

ENTREES/SORTIES

les entrées/sorties sont gérées en C++ sous la forme d'objets, représentant par exemple l'écran, le clavier, un fichier, auxquels on applique des méthodesles classes d'entrées/sorties les plus utilisées sont istream et ostreamil existe 3 objets prédéfinis cin, cout, cerr correspondant respectivement aux fichier standard d'entrée, fichier standard de sortie, fichier standard d'erreurpar défaut, ces fichiers standards sont associés au clavier pour cin, à l'écran pour cout et cerrles méthodes les plus couramment utilisées pour cin et cout sontrespectivement les opérateurs surchargés >> et << pour les typesbool, short, int, long, float, double, char et char*

297

297

ENTREES/SORTIESint main() {

int a;float b;char c;char s[32];cout<<"saisir un entier: ";cin>>a;cout<<"saisir un float: ";cin>>b;cout<<"saisir un caractère: ";cin>>c;cout<<"saisir une chaine de caractères: "cin>>s;. . .

}

298

298

ENTREES/SORTIES

des manipulateurs peuvent être utilisés conjointement avec les opérateurs >> et << pour formater les données saisies ou affichées:endl: : pour insérer le caractère '\n' et vider le buffer de sortiedec : pour passer au mode décimal (mode par défaut)oct : pour passer au mode octalhex:: pour passer au mode hexadécimalws : : pour enlever des espaces de part et d'autre d'une donnéeflush : pour vider le buffer de sortie sur le fichier de sortiesetw(int) : pour indiquer la largeur du champ de la donnéesetprecision(int) : pour indiquer la precision des nombres flottantssetfill(char) : pour indiquer le caractère de remplissage des champs

299

299

ENTREES/SORTIES

int main() {int a=10;char temp[128];

cout<<"a en octal: "<<oct<<setfill('.')<<setw(6)<<a<<endl;cout<<"a en hexadécimal: "<<hex<<setfill('.')<<setw(6)<<a<<endl;cout<<"a en décimal: "<<dec<<setfill('.')<<setw(6)<<a<<endl;const double pi=3.141592654;cout<<setprecision(12)<<pi<<endl;cout<<"saisir une chaine de caractères:"<<flush;cin>>ws>>temp;. . .

}

300

300

ENTREES/SORTIES

les caractères séparateurs pris en compte par l'opérateur >> sont: espace, tabulation, saut de page et retour chariot– il suffit ici d'un espace entre les deux nombre saisis sur la même ligne pour les

attribuer aux variables a et b

int a;double b;cout<<"saisir un entier, puis un flottant: "cin>>a>>b;

301

301

ENTREES/SORTIES

l'opérateur >> renvoie par référence l'objet auquel il est appliqué et null (false) lorsque la fin du flux est atteinte– ceci permet d'effectuer des boucles très simples:

while (cin>>c) cout<<c;

d'autres méthodes que l' opérateur >> sont disponibles dans la classe istream, dont:

istream& get(char& c) pour saisir un caractère quelconque dans cvoid getline(char* buffer,int limite,char delim) pour saisir une chaine de

caractèresvoid putback(char c) pour replacer le caractère c dans le flux

302

302

ENTREES/SORTIES

la manipulation de fichiers consiste à mettre en œuvre des méthodes des classes ofstream, ifstream ou fstreamen plus de méthodes et opérateurs cités précédemment, ces classes disposent de méthodes spécifiques aux fichiersl'ouverture d'un fichier est effectuée simplement lors de la construction d'un objet, sa fermeture doit être explicite

ofstream ficout("beta.txt");char buffer[512];while(cin>>buffer) ficout<<buffer;ficout.close();

303

303

ENTREES/SORTIES

l'ouverture réussie d'un fichier peut être testée simplement par le test de l'objet

ifstream ficin("alpha.txt");char buffer[512];if(!ficin) {

cerr<<"ouverture du fichier impossible!"<<endl;. . .

}else {while(ficin>>buffer) cout<<buffer<<endl;ficin.close();

}

304

304

ENTREES/SORTIES

par défaut, un objet ifstream correspond à un fichier ouvert en mode lecture seule (le mode "r" de la fonction fopen du C)par défaut, un objet ofstream correspond à un fichier ouvert en mode écriture seule (le mode "w" de la fonction fopen du C)d'autres modes d'ouverture sont disponible en indiquant des constantes binaires en argument supplémentaire du constructeurces constantes de classe publiques sont définies dans la classe ios et peuvent être combinées ensemble par l'opérateur sur bits | (OU)

305

305

ENTREES/SORTIES

Constantes de classe de ios équivalence au mode fopenios::in "r"ios::out | ios::trunc "w"ios::out | ios::app "a"ios::in | ios::out "r+"ios::in | ios::out | ios::trunc "w+"ios::in | ios::out | ios::app "a+"

par exemple, pour ouvrir un fichier en mode "a", il suffit d'écrire:ofstream ficout("beta.txt", ios:out | ios::app);

306

306

EXERCICE

Exo34: copie octet par octet d'un fichier dans un autre

307

307

CHAINES DE CARACTERES

les chaines de caractères sont avantageusement traitées en C++ sous la forme d'objets de la classe stringcette classe est définie dans le fichier d'entête string

308

308

CHAINES DE CARACTERES

class string {string();string(const char*);string(const String&);int length() const;int size() const;string substr(index0,index1) const;void insert (index, const string&);void erase (index0, index1);void replace (index0,index1, const string&);string operator+(const string&);String& operator=(const string&);bool empty() const;. . .

};

309

BIBLIOGRAPHIE

310

310

BIBLIOGRAPHIE

C++, La synthèse de G. Clavel, éditions Dunod– très pédagogique– 320 pages en français– idéal pour débuter

Langage C++ de Nino Silverio, éditions Eyrolles– pédagogique– plus de 600 pages en français– assez complet

Programmer en langage C++ de C. Delannoy, éditions Eyrolles– très pédagogique– plus de 600 pages en français– très complet

311

311

BIBLIOGRAPHIE

The C++ programming language de Bjarne Stroustrup, éditions Addison Wesley– élaboré par l'auteur du langage– très complet– plus de 1000 pages en anglais– pas spécialement pédagogique

C++ Primer de S. Lipmann, éditions Addison Wesley– assez pédagogique– très complet– plus de 1200 pages en anglais