Polytech Marseille , Prog. o. o. 1
Introduction à la
Programmation Orientée Objet
Marc DANIEL Ecole Polytech Marseille,
Campus de Luminy, case 925, 13288 Marseille cedex 9 Marc.Daniel@univ-amu.fr
Avril 2021 1
Plan
• Les concepts de la programmation orientée objet 4
– un peu d’histoire 5
– de la nécessité de langages adaptés 7
– vers les langages objets 11
– les principes généraux 13
– la notion de classe 16
– quelques langages 18
• De C à C++ 19
• Les concepts des langages de classes ; implémentation en C++ 55
– les classes et les objets 56
– les opérateurs 90
– l’héritage 104
• La généricité et les modèles 164
Polytech Marseille , Prog. o. o. 3
Plan (suite)
• Quelques compléments 197
– La norme C++ 198
– Les espaces de noms 199
– Amitié, Héritage et Généricité 205
• Les composants prédéfinis 207
– Les flux d’E/S 210
– La classe String 216
– Les conteneurs 221
– Les itérateurs 230
– Les algorithmes 234
– Les traitements numériques 235
• Au sujet de C++11 norme 2011 236
• Conclusion sur C++ 251
• Glossaire partiel 255
• Bibliographie succincte 258
• annexes 259
3
Polytech Marseille , Prog. o. o. 4
Les concepts de la
programmation orientée objet
4
Polytech Marseille , Prog. o. o. 5
Introduction / histoire
• La préhistoire :
– les années 60 et les débuts de la programmation et des langages
• Les années 70-80 : La programmation structurée – analyse descendante
Þ décomposition suivant les traitements
Þ les logiciels sont organisés autour des actions qui traitent les objets
– séparation traitement et données
– interdépendance des données à l’intérieur des traitements – difficile à maintenir
Þ Programme = Algorithmes + données
5
Introduction / histoire
• La programmation et la conception orientée objet Ou un besoin de qualité
– depuis 1975
– essor dans les années 85 (C++, Eiffel) – la consécration à partir des années 90
« La conception objet repose sur une idée apparemment élémentaire. Les systèmes informatiques réalisent certaines
actions sur certains objets. Pour obtenir des systèmes souples et réutilisables, il faut mieux structurer le logiciel
autour des objets que des actions »
Polytech Marseille , Prog. o. o. 7
• Objectifs principaux (Génie Logiciel) : – réutiliser ce qui a déjà été écrit
• directement (le code, les modèles)
• adaptation (l’héritage)
– augmenter la qualité
• rapidité de la conception
• facilité de maintenance
• ergonomie d’utilisation
• fonctionnement simple et efficace Intro. / langages adaptés
développement
utilisation
7
Polytech Marseille , Prog. o. o. 8
• Le logiciel doit être :
• valide
• robuste
• Modifiable (extensible)
• réutilisable
• Portable
• ergonomique
• efficace
• La maintenance doit être facile
• évolution du logiciel (an 2000, Euro, …. !!!), vos futurs stages !!
• correction d’erreurs
Þ Le logiciel doit nécessairement être modulaire
• et … les modules doivent être indépendants Intro. / langages adaptés
8
Polytech Marseille , Prog. o. o. 9
• Dans une application, les coûts les plus importants proviennent :
• De la programmation
• De la maintenance
– Modification des spécifications par le client – Non respect des spécifications
– Tests mal ou pas réalisés – Bugs inévitables
– Adaptation à de nouveaux environnements – …
• Pour la rapidité
• Étude de la complexité
• Loi de Moore (1965) Intro. / langages adaptés
9
• Critères de modularité : – Décomposition modulaire
• la méthode permet de décomposer un problème en sous-problèmes dont les solutions peuvent être recherchées séparément
– Composabilité modulaire
• la méthode fournit des éléments (modules) qui peuvent être combinés pour traiter de nouveaux problèmes
– Compréhensibilité modulaire
• la méthode produit des modules compréhensibles isolément
– Continuité modulaire
• une petite modification de la spécification n’amène des modifications que sur un ou quelques modules. Il ne doit pas y avoir d’impact sur l’architecture du système
– Protection modulaire
Intro. / langages adaptés
Polytech Marseille , Prog. o. o. 11
Une « société » de modules respectant les critères de modularité
• module : unité syntaxique du langage
• un module doit communiquer avec peu de modules
• les échanges entre modules doivent être le plus réduits possibles
• les échanges entre les modules sont clairement explicités dans la définition des modules
• toute information concernant un module est privée, sauf si elle est explicitement déclarée publique
(masquage de l’information)
Intro. / vers les objets
11
Polytech Marseille , Prog. o. o. 12
les modules que l’on appellera objets
• parlent peu
• leurs conversations tiennent en quelques mots
• leurs échanges sont publics, codifiés, donc à haute voix
• la plupart des informations et des traitements sont privés
Intro. / vers les objets
12
Polytech Marseille , Prog. o. o. 13
première vision des objets (modules)
• décomposition en modules sur la base de leurs structures de données
• un module est décrit comme une implémentation de types abstraits de données et des traitements associés
• tout type est un module et vise versa – y compris les types prédéfinis (int, float, …)
– Une structure au sens du C est un module sans traitement : ce n’est pas la logique de l’orienté objet
• chaque module a une interface qui définit comment il dialogue avec les autres
– Ce qui est public – Ce qui est privé
Intro. / principes généraux
13
Programmation orientée objet
• On s’intéresse aux objets avant de s’intéresser aux traitements
– le programme de plus haut niveau sera étudié le plus tard possible
• La p.o.o relève de la création d’objets et de l’exécution d’envois de messages entre les objets
Quels sont les objets ? (il faut définir les bons objets) Quels traitement doivent-ils subir ?
Quels liens ont les objets entre eux ?
Intro. / principes généraux
Polytech Marseille , Prog. o. o. 15
La classe : un moule
• une classe correspond à
– une structure de données abstraites
• partie structurelle composée de champs (appelés attributs ou membres)
– des traitements à effectuer sur ces structures
(appelés méthodes ou fonctions membres) deux méthodes importantes : créateur et destructeur
• services disponibles sur la structure (partie opérationnelle)
• les propriétés de ces services
– une interface (clairement identifiée) qui définit
• partie publique (ce que voit le client)
• les services accessibles auxquels on peut faire appel
• les variables visibles de l’extérieur (en nombre très réduit)
• La façon dont les traitements sont réalisés ne regardent pas le monde extérieur
• on peut modifier les attributs et les méthodes sans modifier l’interface
Intro. / la notion de classe
15
Polytech Marseille , Prog. o. o. 16
• Les classes sont reliées entre elles par des relations d’héritage
– on réutilise – on spécialise
• différentier les traitements
• préciser les attributs
• Les objets sont définis à partir des classes par moulage – instanciation par moulage
– la classe sert de modèle, de moule
• On ne communique avec l’objet qu’en invoquant une des méthodes de son interface
• On a une encapsulation des données et des traitements qui permet l’abstraction des données
• (voir glossaire)
Intro. / la notion de classe
16
Polytech Marseille , Prog. o. o. 17
Une classe très incomplète
class Triangle {
Point _p1, _p2, _p3 ; // données privées public :
...
float aire (…) ; float perimetre (...) ; } ;
• En dehors de la classe, on peut calculer l’aire, le périmètre, mais pas faire référence aux Points _p1, _p2 ou _p3. Si nécessaire : fournir ce qu’il faut dans l’interface (accesseur get_p1, modifieur set_p1).
…
Triangle Mon_triangle ; float area ;
……
area = Mon_triangle.aire(…) ;
• Une classe Triangle_rectangle pourrait hériter de la classe triangle Intro. / la notion de classe
17
Intro. / langages objets
• Simula
• 1966, compilé
• Smalltalk
• années 72-80, interprété --> adapté au prototypage, aussi un système d’exploitation
• C++
• années 85, norme C++ (1998)
• Eiffel
• 1989 (un modèle de langage O.O. resté au stade universitaire)
• Java
• années 95, interprété --> adapté au prototypage
• Ada 95, Lisp, CLOS (Common Lisp Object System)
• C#
Polytech Marseille , Prog. o. o. 19
De C à C++
19
Polytech Marseille , Prog. o. o. 20
De C à C++
• C++ est une amélioration « compatible » de C – qui permet
• l’abstraction de données, la programmation objet
• la généricité, la gestion des exceptions
• ...
– beaucoup plus rigoureux que C – fortement typé
– langage de référence normalisé
– peut être aussi rapide que C (pas de mécanisme sous-jacent complexe)
20
Polytech Marseille , Prog. o. o. 21
De C à C++
• Les moins de C++
– la compatibilité avec C
• langage pouvant être totalement ésotérique
• présence de deux syntaxes
• utilisation des fonctions C
– Attention, certains points du C ANSI sont incompatibles avec C++
• moins permissif
• prototype des fonctions
• les constantes
• …
– langage complexe qui demande un apprentissage progressif
21
De C à C++
• Il n’est pas possible de décrire toutes les possibilités de C++
– long – fastidieux
– certaines techniques sont à éviter
• Il faut utiliser la documentation
– le livre le langage C++ de B. Stroustrup contient un manuel de référence (gros !)
• Il faut « explorer » le langage progressivement au fur et à mesure des besoins
• Toutes les « nouveautés » de C++ ne sont pas forcément des
Polytech Marseille , Prog. o. o. 23
• Un premier programme C++
– using namespace std ;
• compilation et exécution (sous Unix)
De C à C++
#include <iostream>
using namespace std ; int main (void) {
cout << "hello the world" << endl ; return 0 ;
}
libertad:~/essai_C> g++ hello.cxx -o hello libertad:~/essai_C> ./hello
hello the world libertad:~/essai_C>
Remarque sur la norme ….
23
Polytech Marseille , Prog. o. o. 24
De C à C++
• De nombreux mots clés réservés en plus
bool catch class const_cast
delete dynamic_cast explicit false
friend inline mutable namespace
new operator private protected
public reinterpret_cast static_cast template
this throw true try
typeid typename using virtual
wchar_t
• Des opérateurs plus explicites
and (&&) not (!) not_eq (!=) or (||)
……..
(vérifier que ceux-ci sont connus du compilateur)
24
Polytech Marseille , Prog. o. o. 25
De C à C++
• Commentaires
x = y ; // commentaire qui va jusqu’a la fin de la ligne // c’est aussi un commentaire
/* cela marche aussi mais pas de melange SVP
*/
une seule syntaxe pour les commentaires !
• De l’importance – des commentaires
• explicites, complets, nombreux
– des entêtes
• systématiques, homogènes, précis
25
De C à C++
• transtypage
– il existe un transtypage explicite (à privilégier/ transtypage implicite)
en C en C++
int i = 1 ; int i = 1 ;
float x = 6.28 ; float x =6.28 ;
i = (int) x; i = int (x) ;
x = (float) i ; x = float (i) ;
le transtypage de C est parfois indispensable :
ptr1 = char *(ptr2) ; // incorrect car 2 termes ptr1 = (char *) ptr2 ;// correct
– le transtypage a un usage précis et limité (C++ fortement typé) :
Polytech Marseille , Prog. o. o. 27
De C à C++
• Les constantes
– oubliez le C (préprocesseur (#define), portée des constantes)
const int i = 12 ;
– la portée de i est restreinte à l’unité de compilation contenant la définition
.Si nécessaire définir i dans un .h et inclure le fichier (#include …)
– c’est une vraie constante
– Une constante peut être définie par une expression qui peut être évaluée à la compilation
const int nmax = 100 ; const int max = 2 * nmax + 1 ;
– suffixe f (ou F) (float) et u(U) unsigned
const double pi = 3.141596234134567;// les decimales ne sont pas bonnes const float pi_court = 3.141596f ; // les constantes reelles double sinon
27
Polytech Marseille , Prog. o. o. 28
• Déclaration au plus près des identificateurs – déclaration en début de bloc (C)
– déclaration au plus près (attention ensuite au domaine de validité)
…
Prendre des règles de strictes
De C à C++
28
Polytech Marseille , Prog. o. o. 29
De C à C++
#include <iostream>
int main (void) { int n = 100 ;
float y ; y = 10 ; float x ; x = y ;
float z = y ; // attention tout de meme ! for (int i = 0 ; i < n ; i = i + 1)
{
int j = 10 ; n = n + i + j ;
// attention, j est maintenant inconnu } // interdit d'utiliser i ainsi
n = n + i + j ; return 1 ; }
libertad:~/essai_C> g++ portee.c -o portee portee.c: In function `int main()':
portee.c:17: warning: name lookup of `i' changed for new ANSI `for' scoping
portee.c:10: warning: using obsolete binding at `i' portee.c:17: `j' undeclared (first use this function) portee.c:17: (Each undeclared identifier is reported only
once : for each function it appears in.) libertad:~/essai_C>
29
• Les entrées/sorties de base : plus simples qu’en C – <stdio.h> disponible !
– Des flux (flots) prédéfinis dans <iostream> en particulier cin, cout, cerr : on envoie ou on récupère des informations de ces flux
De C à C++ : E/S
#include <iostream>
using namespace std ; int main (void) {
int i = 12 ; float x, y ;
cout << "i=" << i << endl ; cin >> x >> y ;
cerr << "j'avais demande des reels" << endl ;
libertad:~/essai_C> g++ impression.c -o impression libertad:~/essai_C> ./impression
i=12
3 4 j'avais demande des reels
libertad:~/essai_C>
Polytech Marseille , Prog. o. o. 31
• Les entrées/sorties de base : chaîne de caractères
De C à C++ : E/S
#include <iostream>
using namespace std ; int main(void) {
char *ptr = "c'est mieux qu'en C";
char nom [128] ; int i ;
i = 1 ;
cout << "donner le nom et i : " ; cin >> nom >> i ;
cout << "apres lecture" << nom << i <<endl ; cout << ptr << endl ;
return 0 ; }
libertad:~/essai_C> g++ impression2.c -o impression2 libertad:~/essai_C> ./impression2
donner le nom et i : POLYTECH 2 apres lecturePOLYTECH2 c'est mieux qu'en C libertad:~/essai_C>
31
Polytech Marseille , Prog. o. o. 32
• Portée des identificateurs ; opérateur ::
• Attention à la portée des identificateurs et à ce genre de manipulation
De C à C++
#include <iostream>
using namespace std ;
int i = 0 ; // c’est le i global int main (void)
{ int i = 1 ; // la variable locale masque le i global cout << i << endl ; // la variable locale est affichée --> 1 cout << ::i <<endl ; // ::i fait référence à la variable globale
// --> 0 { int i = 2 ; // masque les deux précédentes
cout << i <<endl ; // la variable locale est affichée --> 2 cout << ::i <<endl ; // ::i fait référence uniquement à la
// variable globale --> 0 }
i = 3 ; // modifie le premier i local, le second // n’ayant plus d’existence return 0 ;
}
32
Polytech Marseille , Prog. o. o. 33
• Le type référence
– type dérivée de T : T & type référence vers T
• Une valeur de type référence est une adresse
• Trois différences avec les pointeurs 1) initialisation obligatoire
2) toute opération sur la référence agit sur l’objet référencé (pas sur l’adresse)
3) la valeur de la référence (adresse) ne peut pas être modifiée
• Intérêt : mécanisme d’échange entre fonctions et ….
De C à C++ : les fonctions
int ii = 0 ;
int & rr = ii ; // rr fait donc référence à ii
rr = rr + 1 ; // agit sur ce qui est référencé par rr.
// Donc ii vaut maintenant 1
33
Le prototypage
• C++ est fortement typé
– contrôle systématique entre arguments et paramètres (ou paramètres effectifs et paramètres formels)
• (en nombre, en type)
– prototypage des fonctions est « obligatoire »
• Utiliser le plus possible des fichiers de prototypage (.h)
De C à C++ : les fonctions
// attention la fonction puissance doit retourner une valeur // avec une instruction return
float puissance (float x, int n);
void bidon (int m) ;
Polytech Marseille , Prog. o. o. 35
Paramètres par défaut
• On peut omettre de fournir des arguments
– si des valeurs par défaut ont été définies pour les paramètres – ces paramètres sont les derniers
– on peut passer un argument s’il existe une valeur par défaut – ce gère au niveau du prototype de la fonction
De C à C++ : les fonctions
#include <iostream>
using namespace std ;
void bidon (int, float = 10.0, int = 10) ;
// ou bidon (int m, float a = 10.0, int j = 10) ; int main (void)
{
bidon (1, 1.0, 2) ; bidon (1, 1.0) ; bidon (1) ; return 0 ; }
void bidon(int m, float a, int j) {
cout << "je porte bien mon nom " << m << " " << a << " " << j ; }
35
Polytech Marseille , Prog. o. o. 36
Passage des arguments - mécanisme de correspondance
• En C++, le programmeur a la maîtrise du mécanisme 1) passage par valeur
une copie de l’argument est passé à la fonction
• problème de la copie à l'exécution (savoir le faire et temps)
• ne modifie pas l’original De C à C++ : les fonctions
#include <iostream>
void inutile (int m) {
// définition directe pas de prototypage : place sur le transparent m=m+1 ;
}
int main (void) {
int j = 0 ;
inutile (j) ; // j vaut 0 après l’appel return 0 ;}
36
Polytech Marseille , Prog. o. o. 37
Passage des arguments - mécanisme de correspondance 2) passage par pointeur (C)
• l’adresse de l’argument est passée
• la fonction manipule des pointeurs De C à C++ : les fonctions
#include <iostream>
void bidon (int *m) {
*m = *m + 1 ; }
int main (void) {
int j = 0 ;
bidon (&j) ; // l ’adresse de j est passée. En retour j=1 return 0 ;
}
37
2) passage par référence (C++)
• les paramètres sont de type référence
• les arguments sont directement manipulés De C à C++ : les fonctions
#include <iostream>
void bidon (int & m)
{ // m est une référence
m = m + 1 ; // revient à manipuler l’argument associé }
int main (void) {
int j = 0 ;
bidon (j) ; // Manipulation de l’adresse masquée. En retour j=1 return 0 ;
}
Polytech Marseille , Prog. o. o. 39
Passage des arguments - mécanisme de correspondance Favoriser le passage par référence :
• plus lisible
• le mécanisme à privilégier
• ajout du mécanisme const : la variable ne peut pas être modifiée – équivalent passage par valeur sans recopie
– permet d’éviter les recopies lourdes De C à C++ : les fonctions
#include <iostream>
void bidon (int & m, const int & l) ; // prototypage int main (void)
{
int j = 0 ; int l = 2 ;
bidon (j, l) ; // En retour j=3 et l vaut évidemment 2 return 0 ;
}
void bidon (int & m, const int & l)
{ // m et l sont des références m = l + 1 ; // l est non modifiable }
39
Polytech Marseille , Prog. o. o. 40
Remarques sur l’utilisation de « const »
• const peut aussi être utilisé pour des variables passées par valeur (indépendant du mécanisme de passage)
• signale au compilateur qu’il ne doit pas accepter de modifier des valeurs recopiées ou signalées comme non modifiables
• sécurité de programmation, aide à la correction de programme
• excellente habitude
• Attention à « const type *ptr »
– Signifie ptr est un pointeur sur un objet type constant
– « type *const ptr » signifie que ptr est un pointeur constant vers un objet type !!!
De C à C++ : les fonctions
void bidon (int & m, const float & l, const int n) {
// m et l sont des références // on travaille sur une copie de n // il est interdit de modifier l et n }
40
Polytech Marseille , Prog. o. o. 41
Fonctions en ligne : inline « comme une fonction classique »
• Le compilateur insère le code de la fonction
• quand il rencontre un appel à la fonction (A PRIORI)
• permet d’économiser le mécanisme de transfert d’appel De C à C++ : les fonctions
#include <iostream>
using namespace std ;
inline double valeur_abs (double x) {
return ((x)>0 ? x : -x) ; }
int main (void) {
const double x = 4.0 ; const double x1 = -4.0 ;
cout << "valeur absolue de " << x << "=" << valeur_abs (x) << endl ; cout << "valeur absolue de " << x1 << "=" << valeur_abs (x1) << endl;
return 0 ; }
libertad:~/essai_C> g++ -o inline1 inline1.cxx libertad:~/essai_C> ./inline1
valeur absolue de 4= 4 valeur absolue de -4= 4 libertad:~/essai_C>
41
Fonctions en ligne : inline
• Proche des macro-définitions C (toujours disponibles)
• mieux intégrées dans le langage et typées : à privilégier De C à C++ : les fonctions
#include <iostream>
using namespace std ;
inline void somme_dif (const int a, const int b, int & som, int & dif) {
som = a + b ; dif = a - b ; }
int main (void) {
const int a = 2 ; const int b = 1 ; int u, v ;
somme_dif (a, b, u, v) ; cout << "a+b=" << u << endl ;
libertad:~/essai_C> g++ -o inline2 inline2.cxx libertad:~/essai_C> ./inline2
a+b=3 a-b=1
libertad:~/essai_C>
Polytech Marseille , Prog. o. o. 43
Fonctions en ligne : inline
• des contraintes pour ces fonctions : le code de la fonction doit être disponible à la compilation
– dans le même fichier source (exemple précédent) – dans un fichier .h inclus dans le source
• problème du code dans un .h
– inclus dans le .h avec un include
De C à C++ : les fonctions
// fichier inline.h
inline void swap (int & a, int & b);
#include "inline.hxx"
// fichier inline.hxx void swap (int & a, int & b) {
int c ; c = a ; a = b ; b = c;
#include "inline.h" } int main()
{
int a = 1 ,b = 2 ; swap (a,b) ; return 0 ; }
Même problème pour la généricité
43
Polytech Marseille , Prog. o. o. 44
• Type booléen
– le type bool prend deux valeurs « true » et « false » – les conditions sont des expressions de type bool – conversion implicite entre
• true <--> 1 false <--> 0 ne pas utiliser
• Types struct, enum, (union)
enum jours {lundi,mardi,mercredi, …} ; struct ma_structure {…} ;
…
jours un_jour ; // les valeurs prises par un_jour ne sont // pas « int »
ma_structure str ; // aussi valide enum jours autre_jour ;
struct ma_structure autre_str ;
// conversion implicite donnant la position int i = mercredi ; // i vaut 2
De C à C++ : les types
44
Polytech Marseille , Prog. o. o. 45
• Allocation
– new type_objet alloue un objet de type type_objet
– new type_objet[n] alloue un tableau de n objets de type type_objet
• type-objet est quelconque
• n est une expression arithmétique quelconque
– le résultat d’un new est de type type_objet *
– en cas de problème, il y a lancement d’une exception ...
• Libération
– delete adresse libère l’objet alloué à l’adresse adresse – delete [] adresse libère un tableau alloué à l’adresse adresse – delete et delete [] ne sont pas interchangeables
• Oubliez malloc, calloc, free !
• N’oubliez pas que l’allocation dynamique a un coût ...
De C à C++ : allocation de mémoire
int *c ;
c = new int (10) ; int *c ;
c = new int [10] ;
int *c ;
c = new int [10] ; delete [] c ;
45
C++ et les tableaux
• La déclaration d’un tableau utilise une expression constante
– float V[3] // tableau de 3 réels dans V[0], V[1], V[2]
– char * a[10] // tableau de 10 pointeurs de char (a[0], a[1], …, a[9] )
• Tableaux à plusieurs dimensions (attention au nombre d’indices !) – int mat [3][5] ; tableau de 3 vecteurs entiers de 5 éléments
– // int mat[3,5] ; est incorrect
– utilisation : mat[i][j] i = 0, … 2, j = 0, …, 4
void f (int ind) {
int T[ind] ; // « incorrect » : ind n’est pas une expression constante // il faut dans ce cas allouer dynamiquement le tableau (voir aussi TD) // ou utiliser un composant prédéfini vector qui fait de même
}
Polytech Marseille , Prog. o. o. 47
• Passage d’un tableau en paramètre
#include <iostream>
void f(int t[]) // evidemment t[10] convient ! {
...
}
int main () {
int x[10] = {1,2,3,4,5,6,7,8,9,10};
f(x) ; return 0 ; }
#include <iostream>
void f(int matrice[3][5]) {
...
}
int main () {
int mat[3][5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} ; f(mat) ;
return 0 ; }
C++ et les tableaux C++ et les tableaux
47
Polytech Marseille , Prog. o. o. 48
• Problème des deux (ou plus) dimensions
#include <iostream>
void f(int matrice[][]) // impossible
{ // le calcul de l’adresse de matrice[i][j] est impossible ...
}
#include <iostream>
void f(int matrice[][5])
{ // ici le calcul de l’adresse de matrice[i][j] est possible // cette solution est valide
...
}
C++ et les tableaux
48
Polytech Marseille , Prog. o. o. 49
• Problème des deux (ou plus) dimensions – une autre solution correcte
#include <iostream>
using namespace std ; const int dim_j = 5 ;
void f(int matrice[][dim_j],const int & dim_i) {
int i, j ;
for (i = 0;i < dim_i ; i = i + 1) for (j = 0 ; j < dim_j ; j = j + 1)
cout << matrice[i][j] << endl ; }
int main () {
int mat[3][dim_j]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} ; int dim_i
= 3;
f(mat, dim_i) ; return 0 ; }
C++ et les tableaux
49
• Problème des deux (ou plus) dimensions – la solution la plus générale
#include <iostream>
using namespace std ;
void f(int v[],const int & dim_i, const int & dim_j) {
int i, j ;
for (i = 0;i < dim_i ; i = i + 1) for (j = 0 ; j < dim_j ; j = j + 1)
cout << v[i*dim_j+j] << endl ; // vérifiez le calcul ! }
int main () {
int mat[3][5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} ; int dim_i = 3, dim_j = 5 ;
f(&mat[0][0], dim_i, dim_j) ; return 0 ;
}
C++ et les tableaux
Polytech Marseille , Prog. o. o. 51
• Mémoire allouée à l’éxécution – ne pas oublier de désallouer
à la fin de l’utilisation
• Cas du tableau à plusieurs dimensions
– tableau de tableaux de taille connue à la compilation
#include <iostream>
main() {
int (*mat)[4]; // le type est int (*) [4] ; int i,j ;
i=3 ;
mat = new int[i][4];
// on utilise ensuite mat[i][j]
}
C++ et les tableaux dynamiques
int *c ;
c = new int [10] ; // int d[] ; est incorrect
…
delete [] c ;
// new int [4][i] non : 4 éléments d’un tableau de taille «variable»
// new int [i][j] non
new int [i][4][5] // oui avec un type int (*)[4][5]
// new int [4][i][5] // non // new int [4][5][i] // non
51
Polytech Marseille , Prog. o. o. 52
• Cas du tableau à plusieurs dimensions (2 !) – tableau de tableaux de tailles connues à l'exécution
#include<iostream>
using namespace std ; int main()
{
int l, c ; // On prend un tableau de l lignes et c colonnes int ** mat ; // pointeur sur un pointeur
l = 3 ; c = 4 ;
// tableau des pointeurs vers les vecteurs de c éléments mat=new int*[l];
// allocation des l vecteurs de c éléments for (int i = 0 ; i < l ; i = i +1)
{
mat[i]=new int[c];
for (int j = 0 ; j < c ; j = j +1) mat[i][j]= i + j;
}
for (int i = 0 ; i < l ; i = i + 1) {
for (int j = 0 ; j < c ; j = j + 1) cout << mat[i][j] << "\t" ; cout << "\n" ;
} }
C++ et les tableaux dynamiques
52
Polytech Marseille , Prog. o. o. 53
• Passage du tableau en paramètre
#include <iostream>
void f(int **m, const int & dim_i, const int & dim_j) { // int m[][] n'est pas acceptable
for (int i = 0 ; i < dim_i ; i = i + 1) for (int j = 0 ; j < dim_j ; j = j + 1)
{ } // on peut utiliser ici m[i][j]
}
int main() {
int l, c ; int ** mat ; l = 3 ; c = 4 ;
// tableau des pointeurs vers les vecteurs de c éléments mat=new int*[l];
// allocation des l vecteurs de c éléments for (int i = 0 ; i < l ; i = i +1)
{ mat[i]=new int[c];
for (int j = 0 ; j < c ; j = j +1) mat[i][j]= i + j;
}
f(mat, l, c) ; return 0 ;}
C++ et les tableaux dynamiques
Il faut ensuite évidemment détruire dans l’ordre inverse toutes les zones allouées
53
• Surcharge des fonctions
– offrir plusieurs définitions d’une même fonction (attention aux ambiguïtés)
• les types et/ou le nombre des paramètres doivent être différents : signature
• dans ces conditions, la valeur de retour peut être différente
• le choix de la fonction est géré en fonction des arguments
int max(const int, const int) ; // max de 2 entiers int max (const int, const int, const int) ; // max de 3 entiers // le max du tableau nombre d’éléments : 2ème paramètre
int max(const int [], const int)
// double max(const int, const int) est illégal
// légal le type des paramètres est différent des précédents double max(const double, const double) ;
De C à C++ : surcharges
Polytech Marseille , Prog. o. o. 55
Les concepts des langages de classes ; implémentation en C++
55
Polytech Marseille , Prog. o. o. 56
Les classes : introduction
• Une classe est une implémentation d’un type abstrait de données
• Les classes possèdent :
– des données membres (les attributs) – des fonctions membres (les méthodes)
• Le nom de la classe est le spécificateur de type
• Les objets de type X sont des instances de la classe X
• Au delà des principes généraux il faut un langage d’implémentation
– C++ est un langage difficile et complet
– des variations suivant les langages (en particulier avec Java)
56
Polytech Marseille , Prog. o. o. 57
Les classes : introduction
Masquage de l’information
• Séparation :
– membres et fonctions membres accessibles de l’objet (partie publique)
– membres et fonctions membres propres de l’objet (partie privée)
• L’implémentation d’un objet peut être modifiée sans affecter les applications qui manipulent l’objet
– données membres, fonctions membres Partie privée
Partie publique Vision de l’utilisateur de la classe Vision du concepteur
de la classe
57
Masquage de l’information
• il s’effectue à 3 niveaux – public
– protected
– private (c’est le statut par défaut)
Les classes : introduction
public protected
Accessible aux héritiers Accessible à la classe
private
Accessible à tous
Polytech Marseille , Prog. o. o. 59
Les classes : introduction
Une classe est une unité de compilation
• un fichier entête (Classe.h) qui définit la classe
(appelé interface)• un fichier source (Classe.cxx) qui contient l’implémentation des fonctions membres
• l’implémentation des fonctions peut se faire dans l’entête. Les fonctions sont alors inline par défaut
(se justifie pour des fonctions très courtes)
• Une fonction implémentée dans Classe.cxx
– doit être qualifiée par le nom de la classe par l’opérateur de résolution de portée ::
type Classe::fonction_membre(signature) { …}
59
Polytech Marseille , Prog. o. o. 60
Une première classe : une classe Point
• deux attributs réels – abscisse x et ordonnée y
• en première approche, il faut – initialiser un point avec deux réels – déplacer un point de dx, dy – afficher les coordonnées du point
Les classes : introduction
60
Polytech Marseille , Prog. o. o. 61
Interface de la classe Point (version 1) : Point1.h
• Impossible de consulter ni de modifier les coordonnées
• On peut prévoir des accesseurs (get_x, get_y) ou modifieurs (set_x, set_y)
Les classes : introduction
// interface de la classe Point class Point
{ private :
// on aurait pu choisir de déclarer x et y protected float _x, _y ;
public :
void initialiser (float, float) ; void deplacer (float, float) ; void afficher (void) ;
} ;
61
Implémentation de la classe Point (version 1) : Point1.cxx
Les classes : introduction
// implémentation des fonctions membres de la classe Point //
#include <iostream>
using namespace std ;
#include "Point1.h"
void Point::initialiser (float abscisse, float ordonnee) {
_x = abscisse ; _y = ordonnee ; }
void Point::deplacer(float dx, float dy) {
_x = _x + dx ; _y = _y + dy ; }
void Point::afficher(void) {
Polytech Marseille , Prog. o. o. 63
Appel de la classe point (version 1) : essai_Point1.cxx
Les classes : introduction
#include "point1.h"
using namespace std ; int main (void) {
// déclaration d'un objet point, instance de la classe point Point p ;
// remarquez comment s'appelle une fonction membre p.initialiser (1.0f, 2.0f) ;
p.afficher () ;
p.deplacer (0.1f, -0.1f) ; p.afficher () ;
return 1 ; }
libertad:~/essai_C> g++ -c Point1.cxx libertad:~/essai_C> g++ -c essai_Point1.cxx
libertad:~/essai_C> g++ -o essai_Point1 essai_Point1.o Point1.o libertad:~/essai_C> ./essai_Point1
les coordonnees du point sont : (1,2) les coordonnees du point sont : (1.1,1.9) libertad:~/essai_C>
63
Polytech Marseille , Prog. o. o. 64
Les classes : vie d’un objet
• Un objet, instance d’une classe est : – créé
– initialisé – utilisé
• consulté
• modifié
– libéré – détruit
• Les constructeurs et le destructeur sont des fonctions membres particulières A l’aide d’un des constructeurs
* ( par défaut )
* ou géré par le concepteur
Fonctions membres
A l’aide d’un destructeur géré par le concepteur
64
Polytech Marseille , Prog. o. o. 65
Les classes : vie d’un objet
• Un constructeur est une fonction membre dont le nom est le même que celui de la classe
• Un constructeur n’a pas de type
• On peut avoir plusieurs constructeurs
• En fonction du nombre et du type des arguments passés à l’appel, le bon constructeur est choisi
• Dès qu’il existe un constructeur explicite, le constructeur par défaut de C++ n’est plus accessible
65
Les classes : vie d’un objet
• Un premier exemple de constructeur (1/2)
// interface de la classe Point class Point
{
private : float _x, _y ; public :
Point (float, float) ;
void initialiser (float, float) ; void deplacer (float, float) ; void afficher (void) ;
Point fonction_membre (…) ; } ;
Polytech Marseille , Prog. o. o. 67
Les classes : vie d’un objet
• Un premier exemple de constructeur (2/2)
• On peut aussi utiliser le constructeur – attention à la recopie ...
// implémentation des fonctions membres de la classe Point //
#include "Point1.h"
#include <iostream>
Point::Point (float abscisse, float ordonnee) { _x = abscisse ;
_y = ordonnee ; }…
Point Point::fonction_membre (…)
{ …
return Point (a, b) ; // ce qui crée un nouveau point }
Point p1 (1.0f, 1.0f) ;
Point p2 = Point (1.0f, 2.0f) ;
67
Polytech Marseille , Prog. o. o. 68
Les classes : différents objets
• Objets statiques
– variables globales ou objets déclarés static. Définis à la compilation
• durée de vie = durée de l’exécution du programme
• Objets automatiques – variables locales.
• Appel constructeur : au moment de la définition de l’objet
• Appel destructeur : fin de portée de l’objet
• Objets dynamiques
– définis par un pointeur sur une zone allouée par new
• durée de vie entre le new et le delete
68
Polytech Marseille , Prog. o. o. 69
Les classes : différents objets
• Objets temporaires
– créés à l’exécution ( ex : return (Point (a, b) ) ; )
– création d’un objet temporaire sans nom
• Appel constructeur : au moment de la définition de l’objet
• Appel destructeur : fin de l’expression la plus englobante contenant l’objet
– créés par le compilateur (calculs intermédiaires)
• Nécessite la définition d’un constructeur par recopie
69
Les classes : this
• Paramètre caché de toutes les fonctions membres : this
• implicitement défini (auto-référencé)
• this est un pointeur sur l’objet manipulé – this
– *this
• Son usage est implicite ou explicite
• Accessible à toutes les fonctions membres (non statiques)
Polytech Marseille , Prog. o. o. 71
Les classes : this
Exemples d’utilisation
• L’utilisation de « this » est parfois réellement indispensable – ex : return *this ;
// implémentation des fonctions membres de la classe Point //
#include "Point1.h"
void Point::initialiser (float _x, float ordonnee) {
this->_x = _x ; // indispensable car _x paramètre cache // le membre _x !!
this ->_y = ordonnee ; // correct mais inutile }
void Point::deplacer (float dx, float dy) {
this->_x = this->_x + dx ; // pourquoi faire simple this->_y = this->_y + dy ; // c’est totalement inutile ici }
71
Polytech Marseille , Prog. o. o. 72
Les classes : constructeurs
Trois constructeurs (1/2)
• Le constructeur par défaut – pas de constructeur proposé
• toujours un synthétisé par le compilateur (peut-être dangereux)
– un constructeur sans paramètre proposé
– un constructeur ayant tous ses paramètres par défaut
• le constructeur par défaut doit pouvoir être appelé sans paramètre
– Il est appelé automatiquement dans certaines situations (ex. objets membres)
• Le constructeur avec initialisation
– voir l’exemple de la classe Point
72
Polytech Marseille , Prog. o. o. 73
Les classes : constructeurs
Trois constructeurs (2/2)
• Le constructeur par recopie (d’un autre objet) – le premier paramètre est une référence de l’objet à cloner
classe::classe (const classe & cloneur, .) ou classe::classe (classe & cloneur, …)
• & : pour la « recopie » éventuelle des zones dynamiques de l’objet
• Ne peut pas être passé par copie puisque c’est ce que l’on cherche à définir !
• s’il y a d’autres paramètres, ils doivent tous avoir des valeurs par défaut
• const obligatoire dans certains cas (objets sans nom retourné par recopie)
– Il est conseillé d’avoir un constructeur par recopie
• évite la synthétisation automatique erronée par le compilateur (cas complexes)
• L’appel à un constructeur suit la création de l’objet – en particulier : il a sa place mémoire (sauf les parties dynamiques
éventuelles) 73
Les classes : constructeurs
Un cas particulier important (1/2)
• Les membres de la classe sont des objets (objets membres) – Comment initialiser les objets membres ? (au moment de leur
création)
class Segment { Point a, b ; public :
Segment (float, float, float, float) ;
…};
…Segment::Segment (float x1, float y1, float x2, float y2) : a (x1, y1), b (x2, y2)
{ …} // ainsi les points a et b sont initialisés
…
Polytech Marseille , Prog. o. o. 75
Les classes : constructeurs
Un cas particulier important (2/2)
• Cas de la constante membre (non déclarée static) – Comment initialiser la constante membre ?
class Complexe {
const bool reel ; float _a,_b ;
… public :
Complexe (float xx, float yy) : reel (false) {…}
Complexe (float xx) : reel (true) {…}
...
};
– reel est initialisée à la création et ne peut plus être modifiée
• Même problème pour les membres de type reference
75
Polytech Marseille , Prog. o. o. 76
Les classes : destructeur
Le destructeur
• le destructeur de la classe « Classe » est la fonction membre de nom :
~Classe (void)
• Il n’a pas de type ni de paramètre
• Il libère en particulier la mémoire allouée
• L’appel au destructeur précède la destruction de l’objet
• Une classe peut ne pas avoir de destructeur
– toujours un synthétisé par le compilateur (peut être dangereux)
76
Polytech Marseille , Prog. o. o. 77
Les classes : synthèse constructeurs et destructeur
Interface de la classe Point (version 2) : point2.h
// interface de la classe Point class Point
{ float _x, _y ; public :
// les constructeurs
Point (void) ; // constructeur par défaut Point (const float) ; // un deuxième constructeur Point (const float, const float) ; // encore une surcharge Point (const Point &) ; // constructeur par recopie
// ce constructeur pourrait // avoir d’autres paramètres
// avec des valeurs par défaut ! void initialiser (float, float) ;
void deplacer (float, float) ; void afficher (void) ;
// le destructeur
~Point (void) ; };
77
Les classes : synthèse constructeurs et destructeur
Implémentation de la classe Point (version 2) : Point2.cxx
// implémentation des fonctions membres de la classe Point
// #include <iostream>
#include "Point2.h"
using namespace std ; Point::Point(void) { _x = 0.0 ;
_y = 0.0 ;
cout << "constructeur par defaut" << endl ; } // ou Point::Point(void): _x(0),_y(0) {…}
Point::Point(const float r) {
_x=r ; _y=r ;
cout << "constructeur 1 parametre" << endl ; }
// ou Point::Point(const float r): _x(r),_y(r) {…}
...
Point::Point(const float r, const float t) {
_x = r ; _ y = t ;
cout << "constructeur 2 parametres" << endl ; }
// ou Point::Point(const float r, const float t) : _x(r),_y(t) { …}
Point::Point (const point & p) { _x = p._x ;
_y = p._y ;
cout << "constructeur par recopie" << endl ; }
Point::~Point (void)
{ cout << "destructeur" << endl ; }
Polytech Marseille , Prog. o. o. 79
Appel de la classe Point
(version 2) : essai_point2.cxx
Les classes : synthèse constructeurs et destructeur
#include "Point2.h"
Point a ;
//--- Point creepoint(void)
{
Point p = Point (7.0, 8.0) ; return (p) ;
}
//--- int main(void)
{
Point b(5.0), c(5.0, 2.0), e(b), f = c ; Point * d ;
a.afficher( ); b.afficher( ); c.afficher( );
d=new Point(6.0); d->afficher( ); delete d ; e.afficher( ); f.afficher( );
f =creepoint( ); f.afficher( );
return 1 ; }
libertad:~/essai_C> ./essai_point2 constructeur par defaut constructeur 1 parametre constructeur 2 parametres constructeur par recopie constructeur par recopie
les coordonnees du point sont : (0,0) les coordonnees du point sont : (5,5) les coordonnees du point sont : (5,2) constructeur 1 parametre les coordonnees du point sont : (6,6) destructeur
les coordonnees du point sont : (5,5) les coordonnees du point sont : (5,2) constructeur 2 parametres constructeur par recopie destructeur
destructeur
les coordonnees du point sont : (7,8) destructeur
destructeur destructeur destructeur destructeur libertad:~/essai_C>
?
?
79
Polytech Marseille , Prog. o. o. 80
Les classes : opérations par défaut On peut :
• Instancier des objets et utiliser un constructeur :
class C
{ …} ; // déclaration de la classe C ...C objet1, objet2 ;
• Pointer sur un objet Faire référence à un objet
C * p ; C & ref = objet2 ;
p = & objet1 ;
• Affecter un objet
objet1= objet2 ;
– lors de l’affectation, il n’y a pas, par défaut, de recopie de la partie dynamique éventuelle. Il y a juste affectation du pointeur
– Si nécessaire, gestion explicite obligatoire par surcharge de l’opérateur d’affectation
80
Polytech Marseille , Prog. o. o. 81
Variable membre statique
• Variable partagée par tous les objets de la classe – stockée à un seul endroit et initialisée
• hors de la classe et hors de toute fonction membre
• Il existe aussi des constantes membres static Les classes : membres statiques
// interface de la classe Point class Point
{ float _x, _y ;
static int _nb_points ; // ou public public :
// les constructeurs
Point(void) ; // le destructeur...
~Point(void) ; };
// fonctions membres de la classe Point int Point::_nb_points = 0 ;
Point::Point(void) { _x = 0.0 ;
_y = 0.0 ;
_nb_points = _nb_points + 1 ; }
Point::~Point(void)
{_nb_points = _nb_points - 1 ; }...
81
Fonction membre constante : important
• Fonction qui peut consulter l’objet mais pas le modifier
type Classe::nom_fonction (signature) const ;
• Peut s’appliquer sur un objet const (autrement : illégal) Les classes : fonctions membres
// interface de la classe Complexe class Complexe
{ float _a, _b ; public :
...float get_a(void) const { return _a ; } float get_b(void) { return _b ; } };
#include "Complexe.h"
….
const Complexe c(1.0,1.0) ;
float x = c.get_a () ; // autorisé // float y = c.get_b () ; est illégal
Polytech Marseille , Prog. o. o. 83
Fonction membre statique
• Fonction qui manipule des membres statiques et peut ainsi être déclarée static
– utilisable partout par la référence à la classe – pas d’accès à this
Les classes : fonctions membres
// interface de la classe Point class Point
{ float _x, _y ;
static int _nb_points ; public :
…
static void afficher_compteur(void) ; };
// fonctions membres de la classe Point
int Point::_nb_points = 0 ; void Point::afficher_compteur(void) {
cout << _nb_points << endl ; }
...
// peut ensuite être appelée // n’importe où
...
Point::afficher_compteur() ; ...
83
Polytech Marseille , Prog. o. o. 84
Les classes : des membres et fonctions membres !
On peut aussi avoir :
• des membres mutable
– modifiable même s’il appartient à un objet const
• (ex : nb_consultations d’un objet)
• des pointeurs sur des membres
• des pointeurs sur des fonctions membres – l’identificateur d’une fonction est un pointeur – la valeur du pointeur peut changer
• même syntaxe pour différents appels !
• « lire la doc ! »
84
Polytech Marseille , Prog. o. o. 85
Les classes : tableaux d’objets
• On peut créer des tableaux d’objets – à 1, 2 ou n indices
– conformes aux tableaux vus dans la partie précédente – statiques, automatiques ou dynamiques
• Une bonne règle : l’objet doit avoir un constructeur par défaut
• On peut créer des opérations de « haut niveau » !
class Complexe {
…public
Complexe(float = 0.0, float = 0.0) ; // constructeur par défaut
…
}; ….
Complexe ztab[100] ; ...
85
Les classes : l’amitié en C++
• Une entorse aux concepts de P rogrammation O rientée O bjets …
• la partie private est réservée aux instances de la classe.
• Les amis ou friend permettent de casser le mécanisme d’encapsulation et d’accéder aux informations de la classe
• Les friend peuvent être : – des fonctions indépendantes
– des fonctions membres d’une autre classe – des fonctions amies de plusieurs classes – des classes amies
• L’abus des « friend » est dangereux. A consommer avec
Polytech Marseille , Prog. o. o. 87
Les classes : l’amitié en C++
• Un exemple pour « améliorer la performance »
class Tableau {
int *tab, taille ;
// définie ici ou en partie public friend void print(const Tableau &) ;
public :
Tableau(int n = 10) { taille = n ; tab = new int [taille] ; } // lancement d’une exception si problème d’accès
int & element(int i) { assert ( i < taille) ; return tab[i] ; } } ; …
void print(const Tableau & t)
{// « plus rapide » que de passer par la fonction membre element for (int i = 0 ; i < t.taille ; i = i + 1)
cout << t.tab [i] ; }
87
Polytech Marseille , Prog. o. o. 88
• Un exemple de classe amie : une liste de chainons
class Chainon {
char *donnee ; // ou char donnee ; Chainon * suivant ;
friend class Liste ; } ;
class Liste
{ Chainon * tete ; public :
Liste (void) {
tete = new Chainon ; tete->suivant = NULL ; // accès au membre privé de tete } ; }
• Remarque : exemple incomplet pour être opérationnel Les classes : l’amitié en C++
88
Polytech Marseille , Prog. o. o. 89
Les classes : l’amitié en C++
• Attention : Les amis de mes amis ne sont pas mes amis
class A {
static const int _n = 100 ; friend class B ;
} ;
// la définition de la classe B est correcte class B
{ float tab[A::_n] ; friend class C ; } ;
// la définition de la classe C est incorrecte // il faudrait que C soit amie de A
class C {
int t [A::_n] ; } ;
89
Les opérateurs
• Idée : étendre les opérateurs de base pour qu’ils puissent s’appliquer aux objets
– (ex : pour 2 complexes z1 et z2 z1 = = z2 z1 + z 2 )
• Il existe des opérateurs – unaires &, new – binaires = =, <, <<, >>
– unaires et binaires +, -, * (pointeur), * (multiplication)
• Les opérateurs ont un ordre de précédence (priorité) – z = a * b + c ; veut dire a * b
puis (a * b) + c
Polytech Marseille , Prog. o. o. 91
Les opérateurs
• Les opérateurs unaires sont associatifs à droite
*p++ ; veut dire *(p++) ; (sans commentaire !)
= est associatif à droite : a = b = c veut dire a = (b = c)
• Les opérateurs binaires sont associatifs à gauche a + b + c ; veut dire (a + b) + c ;
• « lire la doc ! » pour – liste des opérateurs – les ordres de précédence
• La programmation explicite des précédences et associativités est la règle pour les cas non triviaux
91
Polytech Marseille , Prog. o. o. 92
Les opérateurs
• Les opérateurs prédéfinis peuvent être redéfinis pour les types de l’utilisateur donc pour les objets
• Les opérateurs sont des fonctions
x = y + z ; veut dire operator= (x, operator+ (y, z) ) ;
• Surcharger un opérateur : créer une nouvelle fonction – les règles des fonctions sont conservées
– les précédences, arités et associativités ne peuvent être modifiées – ne conserve pas nécessairement l’éventuelle commutativité
• Il est impossible de créer de nouveaux opérateurs
• Les opérateurs d’affectation (=) et d’adressage (&) sont implicitement définis, mais peuvent être redéfinis
– attention à l’implicite, ex : objet avec partie dynamique
92
Polytech Marseille , Prog. o. o. 93
Les opérateurs
• Les opérateurs ne peuvent pas être surchargés pour les types prédéfinis (int, …)
• Pas de surcharge possible pour
– . Sélection de membre ( objet.membre ) – .* ( objet.pointeur_de_membre )
– :: résolution de portée ( Classe::nom ) , global ( ::nom ) – ? : expression conditionnelle ( x>0 ? x : -x )
– sizeof taille de l’objet ( sizeof expr ), d’un type (sizeof (type) ) – #, ## gérés par le préprocesseur
93
Deux implémentations possibles, mais différentes (1/2)
• redéfinir l’opérateur par une fonction membre
– implicitement, l’opérateur redéfini a un paramètre de plus (this) – pas de symétrie naturelle -> pas commutatif
– si le membre gauche est d’une autre classe : impossible – non static (sauf éventuellement new, new [], delete, delete [])
class Complexe { float _a, _b ; public :
Complexe(float = 0.0, float = 0.0) ; Complexe operator+(const Complexe &) ; } ; …
Complexe Complexe::operator+(const Complexe &z) { return Complexe (_a + z._a, _b +z._b) ; }
Les opérateurs
…Complexe z1, z2, z3 ; float x ;
// possible z3 = z1 + z2 ;
// possible avec conversion // implicite de type // x-> complexe (avec un // constructeur 1
paramètre) z3 = z1 + x ;
Polytech Marseille , Prog. o. o. 95
Les opérateurs
…Complexe z1 ; Complexe z2 ; Complexe z3 ; float x ; // possible z3 = z1 + z2 ; // possible avec // la conversion // implicite de x z3 = x + z1 ; z3 = z1 + x ;
Deux implémentations possibles, mais différentes (2/2)
• redéfinir l’opérateur par une fonction non membre
– la fonction « doit être » amie pour accéder simplement aux membres – symétrie naturelle
– plus d’accès à this
class Complexe { float _a, _b ; public :
Complexe(float = 0.0, float = 0.0) ; friend Complexe operator+(const Complexe &,
const Complexe &) ;
… } ;
Complexe operator+(const Complexe & z1, const Complexe & z2)
{ return Complexe(z1._a + z2._a, z1. _b +z2._b) ; }
95
Polytech Marseille , Prog. o. o. 96
Les opérateurs
Point & operator = (const Point & p) {
_x = p._x ; _y = p._y ;
return *this ; // pourquoi * this ? }
opérateur d’affectation
• Le type de retour d’une expression d’affectation est le type de l’opérande gauche avec une référence pour éviter la copie
• L’opérande gauche « doit » être une lvalue modifiable – Pour pouvoir affecter !
• Ne pas confondre
a = b ; et Point a = b ;• Attention, par défaut un opérateur d’affectation est synthétisé
– Copie membre à membre ….. y compris les pointeurs sur les parties dynamiques On pourrait écrire :
void operator= (…)
mais a=b=c soit : a=(b=c) serait impossible
Point operator = (…)mais a=b=c impliquerait la copie de b
96
Polytech Marseille , Prog. o. o. 97
lvalue et fonction de type référence
• A l’origine (d’autant que C++11 modifie les choses)
« Une lvalue est quelque chose qui peut être du côté gauche d’une affectation » (par opposition à une rvalue)
• Plus précisément
« Un objet est une région de mémoire ; une lvalue est une expression qui fait référence à une zone mémoire », qui a donc une persistence dans le temps. On ne peut pas tout faire avec des rvalues !
• Une lvalue peut être modifiable ou non (par exemple si elle est déclarée constante)
• Certains opérateurs produisent des lvalues
• Une fonction de type référence est une fonction retournant une lvalue, quelque chose qui « reste »
– Et qui évite une recopie
Les opérateurs
int & f(….) {...} // f retourne une lvalue de type int i=3 ;
lvalue= rvalue ;
97
opérateur d’indexation
• Permet d’accéder directement aux éléments d’un tableau à l’extérieur de la classe
Les opérateurs
#include <iostream>
using namespace std ; class Vecteur { private :
int _T[10];
public :
Vecteur(){ …}
int & operator[](const int & i) { return _T[i] ;}
};
int main () {
Vecteur t ; int i = 1;
//cout << t._T[1] ; // accès à un variable privée; OK si en public cout << t[i] <<endl;