Upload
dinhnguyet
View
216
Download
0
Embed Size (px)
Citation preview
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
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
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
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
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
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;
}
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
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
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
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();
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
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
}};
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; }
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
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)
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
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
}
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;}
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(…)
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
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;
. . .};
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
}
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
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;}
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
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 . . .};. . .
}. . .
};
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:. . .
};
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;
}};
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}
};
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)
{}
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
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>
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);
}
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;
. . .}
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);
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;. . .
};
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