Structures de données IFT-10541

Slides:



Advertisements
Présentations similaires
A RECUPERER EN ENTRANT Le polycopié de Caml Partie 1
Advertisements

Introduction Langage très répandu Noyau Linux VLC … Des avantages indéniables mais aussi des contraintes ! Ceci nest quun rapide tour.
Programmation Orienté Objet en C++
Rappels C.
Introduction au langage C
Patrick Reuter maître de conférences
la programmation en langage C
Exercice 1 1 (père) et 1 (fils) 1 (père) et 0 (fils)
1 Une introduction à Java IFT 287 (Semaine 1). 2 Java - Historique Développé par Sun Microsystems en 1994 –Inventeur James Gosling (canadien!) Objectif.
Le langage C++ Langage orienté objet
Programme de baccalauréat en informatique Algorithmique et programmation IFT-1001 Thierry EUDE Hatem Mahbouli Laboratoire #12 Département dinformatique.
Structures de données IFT-2000
Bases de la programmation en C++
HistoriqueHistorique Langage C++, parution du livre Bjarne Stroustrup Normalisation ANSI.
Programmation Initiation Aux Langages C et C++ (3) Bruno Permanne 2006.
Introduction Création d'un projet Ajout de fichiers Compilation Exécution Utiliser le débogueur Département dinformatique et de génie logiciel Développer.
Algorithmique et Programmation
IFT-2000: Structures de données Plan de cours Théorie du contrat Types abstraits Dominic Genest, 2009.
Structures de données IFT-2000
Structures de données IFT Abder Alikacem La classe string Département dinformatique et de génie logiciel Édition Septembre 2009 Département dinformatique.
Structures de données IFT Abder Alikacem Espace de nommage Département d’informatique et de génie logiciel Édition Septembre 2009.
Structures de données IFT-2000
Structures de données IFT-2000 Abder Alikacem Espace de nommage Département dinformatique et de génie logiciel Édition Septembre 2009 Département dinformatique.
Structures de données IFT-2000
Programme de baccalauréat en informatique Programmation Orientée Objets IFT Thierry EUDE Module 5 : La surcharge des opérateurs Département dinformatique.
Structures de données IFT-10541
Structures de données IFT-2000
Structures de données IFT-2000
Structures de données IFT Abder Alikacem Gestion des exceptions Département dinformatique et de génie logiciel Édition Septembre 2009.
Structures de données IFT-10541
Structures de données IFT-2000
Structures de données IFT-2000 Abder Alikacem Sous-séquence de somme maximale (Algo#4). Algorithme retour arrière. Département dinformatique et de génie.
Structures de données IFT-10541
Structures de données IFT-2000
IFT Structures de données
Miguel Garzon CrUise Lab - SITE. Introduction Data Types and Sizes Constants Logic Operators Type conversions Example.
1 La récursion. Nous avons vu qu'un programme est constitué d'un ensemble de fonctions. Il est possible pour une fonction donnée d'appeler une autre fonction.
Structures de données IFT-2000
IFT Structures de données Abder Alikacem Département dinformatique et de génie logiciel.
Structures de données IFT-10541
Structures de données IFT-2000 Abder Alikacem La récursivité Semaine 5 Département dinformatique et de génie logiciel Édition Septembre 2009.
Structures de données IFT Abder Alikacem Gestion des exceptions Module 2 Département dinformatique et de génie logiciel Édition Septembre 2009.
Structures de données IFT Abder Alikacem Linéarisation des matrices Département dinformatique et de génie logiciel Édition septembre 2009.
Structures de données IFT-2000 Abder Alikacem L’héritage en C++ Département d’informatique et de génie logiciel Édition Septembre 2009.
Structures de données IFT Abder Alikacem Semaine 10 Les arbres SPLAY Département dinformatique et de génie logiciel Édition septembre
Structures de données IFT-2000
Une introduction à Java
Structures de données IFT-2000 Abder Alikacem La récursivité Département d’informatique et de génie logiciel Édition Septembre 2009.
Structures de données IFT-10541
Analyse d’algorithmes
Structures de données IFT-2000
Formation C++. Hello World ! #include /* Commentaire sur plusieurs lignes */ int main() { //Affiche hello world std::cout
Introduction à la programmation orientée objets
Structures de données IFT-2000
Structures de données IFT-2000
Structures de données IFT-2000 Abder Alikacem Introduction Semaine 1 Département d’informatique et de génie logiciel Édition Septembre 2009.
Structures de données IFT Abder Alikacem Semaine 2 Tests sur les pointeurs Département d’informatique et de génie logiciel Édition Janvier 2009.
L’essentiel du langage C
Programmer en langage c
ALGORITHMIQUE ET PROGRAMMATION C
Argc et argv Utilisation des paramètres de la ligne de commande.
Introduction à la programmation objet en C++
HistoriqueHistorique Langage C++, parution du livre Bjarne Stroustrup Normalisation ANSI.
CSI2520 Un langage de programmation impérative et concurrente.
L’ histoire: 1976: - Bell Labs abandonne le projet MULTICS. - Ken Thompson, programmeur système chez Bell Labs, se trouve désoeuvré. MULTICS devait être.
Conception de Programmes - IUT de Paris - 1ère année Cours 2 – Références et passage de paramètres Les Références –Introduction aux références.
Conception de Programmes - IUT de Paris - 1ère année Conception de Programmes Objectifs et organisation du cours Introduction à la P.O.O. et au.
Conception de Programmes - IUT de Paris - 1ère année Conception de Programmes Objectifs et organisation du cours Introduction à la P.O.O.
Structures de données IFT-2000 Abder Alikacem Laboratoire #1 Département d’informatique et de génie logiciel Édition Septembre 2009.
Int 10. Int 6.
Transcription de la présentation:

Structures de données IFT-10541 Abder Alikacem Introduction au langage C++ Édition Septembre 2009 Département d’informatique et de génie logiciel

Plan Introduction Un C amélioré Nouveaux types Techniques objets

Introduction Cette partie du cours suppose comme pré requis le langage C. Quelques vérités la programmation orientée objet n‘est pas le C++ on peut programmer « orienté objet » en C++ on peut programmer « sans objets » en C++

Introduction C date de 1972 son créateur est Dennis Ritchie(Bell Labs) il a été normalisé en 1989 ANSIX3.159 1989 C++ date de 1980 son créateur est Bjarne Stroustrup (Bell Labs)

Introduction « hello world » en C++ #include <stdio.h> int main(){ printf("hello world\n"); return 0; } #include <iostream> int main(){ cout << "hello world" << endl; return 0; }

Un C amélioré Commentaires Nombre variable d’arguments Constantes Déclarations et définitions Prototypage de fonctions Surdéfinition de fonctions Arguments par défaut Fonctions « en ligne» Passage par référence Nombre variable d’arguments Types composés Conversion de types Résolution de portée Edition de liens Allocation dynamique Fonctions génériques Entrées-sorties Différences entre C et C++

Deux types de commentaires … Un C amélioré Les commentaires Deux types de commentaires … type« bloc» type« ligne» /* blablabla blablabla */ // blablabla Une« surprise», que fait ceci en C et en C++ ? x = a //* divisons par b */b

Un C amélioré Les constantes L ‘utilisation du préprocesseur C est une source d’erreurs difficiles à détecter. En C++ l ‘utilisation du préprocesseur est réduite à: la compilation conditionnelle et à l ‘inclusion de fichiers #ifdef toto … #endif #include « toto.h

Un C amélioré Les constantes Le préprocesseur n ‘est plus utilisé pour définir des constantes avec des « #define» dans les .h En C++ il y a le mot clé « const » pour définir les constantes. #define K 1024 const int K = 1024; « const » signifie que la donnée définie ne peut être modifiée

Un C amélioré Les constantes Les contrôles concernant les constantes sont faits statiquement à la compilation et le fait que les constantes soient déclarées comme des variables n‘affecte en rien les performances du programme au moment de l‘exécution. Il est donc inutile en C++ (bien que cela soit possible) de conserver des constantes du genre « #define» pour tenter d‘économiser de la place ou accélérer le programme. Il vaut mieux confier ce type d‘optimisations au compilateur.

Un C amélioré Les constantes const int i = 7; const double pi = 3.1416; const char titre[] = "toto"; const int x; i++; titre[3] = ‘ a ’;

Un C amélioré Les pointeurs constants int i = 0; // variable int j; int* const iptr = &i; i=1; //ok *iptr = 2; // ok iptr = &j; // non Pointeur constant sur une variable

Un C amélioré Les pointeurs constants const int n = 0; // constante const int p = 1; // constante int* const nptr = &n; // non const int* const pptr = &p; // ok *pptr = 2; // interdit pptr = &n; // interdit pptr = nptr; // interdit Pointeur constant sur une constante

Un C amélioré Déclarations et définitions de variables const int N = 10; int i; for(i=0; i<N; i++) {…} int deuxN = N *2; for(int j=0; j<deuxN; j++) {…} if (int k = get()) { i=k;} else {j= k; } while (int m = get()) { i += m}; switch (int n = get()) { case 0: i = 1*n; break; case 1: i = 2*n; break; default: i = 3*n; break; } On déclare une variable quand on en a besoin.

Un C amélioré surdéfinitions de fonctions Il est permis de définir dans un même programme deux ou plusieurs fonctions portant le même nom. Cela implique que ces fonctions doivent Différer au niveau de leur prototype/signature double add(double a, double b){ return a + b; } int add(int a, int b){ return a + b; } (int, add, int, int) (double, add, double, double) int i = add(1, 2); double d = add(1.0, 2.0);

Un C amélioré Arguments par défaut Les arguments d’une fonction peuvent avoir des valeurs par défaut pour les paramètres formels de fin. Si, lors d’un appel, les paramètres correspondants ne sont pas spécifiés, alors les valeurs par défaut sont utilisées. double add(double a, double b = 2.0){ return a + b; } double d = add(1.0, 2.0); double dd = add(1.0);

Un C amélioré Fonctions « en ligne » Il est possible de définir des fonctions « en ligne ». Le compilateur, à chaque appel de la fonction mise « en ligne », ne provoque pas un appel classique de fonction mais insère à cet endroit le code de la fonction dans lequel les paramètres formels sont remplacés par les paramètres effectifs. inline double add(double a, double b){ return a + b; } La mise « en ligne » permet d’Accélérer l’exécution d’un programme en supprimant l’appel de fonction, le passage des paramètres dans la pile, puis le retour de fonction.

Un C amélioré Passage d’arguments par référence En C++, on peut passer par référence sans avoir à manipuler l’adresse explicitement en utilisant un synonyme d’une variable. void add(int a, int b, int &sum) { sum = a + b; } int A, B, SUM; A = 0; B = 1; SUM = 2; cout << SUM << endl; add(A, B, SUM);

Un C amélioré Conversion de types Les conversions de type sont explicites ou implicites Le programmeur « décide » Le compilateur « décide »

notation fonctionnelle Un C amélioré Conversion de types Les conversions explicites (type) expression notation fonctionnelle (type) expression Comme en C a) int i; long l; l = long(i); int i; long l; l = (long) i; b) #include <stdio.h> int main() { for(int i = 0; i <=255; i++) printf("%d --> %c", i, char(i)); return 0; }

Un C amélioré Conversion de types Les conversions explicites Le « haut de gamme » de la conversion … const_cast<T>(e) Comme T(e) ou (T)e mais ne modifie que « const » ou « volatile » const char *a = "abcd"; char *b = a; // NON char *c = const_cast<char *>a; // OK

Un C amélioré Conversion de types Les conversions explicites Le « haut de gamme » de la conversion … reinterpret_cast<T>(e) Utilisé pour réinterpréter complètement un type: ce n’est Pas une simple conversion entre nombres entiers et/ou réels int i; char *cptr; cptr = reinterpret_cast<char *>(i);

Un C amélioré Conversion de types Les conversions explicites Le « haut de gamme » de la conversion … dynamic_cast<T>(e) Réservé aux objets, sera vu plus tard

Un C amélioré L’opérateur de résolution de portée « :: » Permet d’accéder à des identificateurs globaux qui seraient Masqués par des identificateurs locaux. int i; int f() { i = ::i; } i local i gobal

Un C amélioré L’édition des liens La gestion des symboles concernant les noms ne se fait absolument plus comme en C! En C, à toute fonction toto() correspond un symbole _toto qui permet de linker un programme appelant avec une bibliothèque contenant le code(et donc Le symbole _toto) de la fonction toto(). En C++, à cause du concept de surdéfinition de fonction, cela n ‘est plus possible. En effet, comment différencier à l‘aide du symbole _add les deux fonctions add(int, int) et add(float, float) ?

Un C amélioré L’édition des liens Le C++ crée donc des symboles spéciaux qui représentent la signature des fonctions au lieu des noms de fonctions. Cela s ‘appelle le name « mangling». L ‘opération inverse, passer du nom de la signature au nom de la fonction s ‘appelle le name « demangling». Chaque compilateur peut avoir sa propre convention du mangling/demangling. On ne peut pas toujours linker un code compilé avec un compilateur C++ avec un code compilé avec un autre compilateur C++ sur une même machine. On ne peut debugguer un code C++ que si le debugger C++ sait faire le demangling inverse du compilateur utilisé pour fabriquer le code de l’application. Il est impossible de linker simplement du C++ avec du C ou l’inverse.

Un C amélioré L’édition des liens Conscients de ces problèmes, les concepteurs du C++ ont prévu l’outillage permettant d’interfacer simplement du C++ avec du C. Header C++ extern "C" { int add_int_C(int, int); float add_float_C(float, float); } Fichier C++ Le code de add_int_C et add_float_C se trouve dans un fichier C. int i = add_int_C(1, 2); float f = add_float_C(1.0, 2.0);

Un C amélioré Allocation dynamique L’allocation dynamique fait partie intégrante du C++. Ce ne sont plus des Fonctions qu’il faut savoir utiliser comme malloc(), realloc, fre()… Deux opérateurs sont prévus: new et delete. struct Toto { int i; float f; }; typedef Toto* TotoPtr; TotoPtr t = new Toto; // équivalent à (TotoPtr) malloc(sizeof(structToto)). ... t->i = 1; t->f = 1.0; delete t; // équivalent à free(t);

Un C amélioré Allocation dynamique L’allocation des tableaux est quasiment identique. Deux opérateurs sont prévus: new [] et delete []. struct Toto { int i; float f; }; typedef Toto* TotoPtr; TotoPtr t = new Toto[10]; ... t[0].i = 1; t[0].f = 1.0; delete [] t;

Un C amélioré Allocation dynamique L’opérateur new[] permet également d’allouer des tableaux à plusieurs dimensions. Pour cela, il suffit de spécifier les tailles des différentes dimensions à la suite du type de donnée des éléments du tableau, exactement comme lorsque l’on crée un tableau statiquement. Toutefois, seule la première dimension du tableau peut être variable, et les dimensions deux et suivantes doivent avoir une taille entière positive et constante. Par exemple, seule la deuxième ligne de l’exemple qui suit est une allocation dynamique de tableau valide : int i=5, j=3; int (*pi1)[3] = new int[i][3]; // Alloue un tableau de i lignes de trois entiers. int (*pi2)[3] = new int[i][j]; // Illégal, j n’est pas constant. Si l’on désire réellement avoir des tableaux dont plusieurs dimensions sont de taille variable, on devra allouer un tableau de pointeurs et, pour chaque ligne de ce tableau, allouer un autre tableau à la main (dans une boucle).

Un C amélioré Allocation dynamique L’opérateur new[] permet également d’allouer des tableaux à plusieurs dimensions. Pour cela, il suffit de spécifier les tailles des différentes dimensions à la suite du type de donnée des éléments du tableau, exactement comme lorsque l’on crée un tableau statiquement. Toutefois, seule la première dimension du tableau peut être variable, et les dimensions deux et suivantes doivent avoir une taille entière positive et constante. Par exemple, seule la deuxième ligne de l’exemple qui suit est une allocation dynamique de tableau valide : int i=5, j=3; int (*pi1)[3] = new int[i][3]; // Alloue un tableau de i lignes de trois entiers. int (*pi2)[3] = new int[i][j]; // Illégal, j n’est pas constant. Si l’on désire réellement avoir des tableaux dont plusieurs dimensions sont de taille variable, on devra allouer un tableau de pointeurs et, pour chaque ligne de ce tableau, allouer un autre tableau à la main (dans une boucle).

Un C amélioré Allocation dynamique Attention, il ne faut pas mélanger new/delete avec malloc/free. Il ne faut pas mélanger non plus new/delete et new []/delete []. Sinon, le comportement du programme n’est plus garanti. Il est important donc d’utiliser l’opérateur delete[] avec les pointeurs renvoyés par l’opérateur new[] et l’opérateur delete avec les pointeurs renvoyés par new. De plus, on ne devra pas non plus mélanger les mécanismes d’allocation mémoire du C et du C++ (utiliser delete sur un pointeur renvoyé par malloc par exemple). En effet, le compilateur peut allouer une quantité de mémoire supérieure à celle demandée par le programme afin de stocker des données qui lui permettent de gérer la mémoire. Ces données peuvent être interprétées différemment pour chacune des méthodes d’allocation, si bien qu’une utilisation erronée peut entraîner soit la perte des blocs de mémoire, soit une erreur, soit un plantage.

Un C amélioré Allocation dynamique L’opérateur new[] alloue la mémoire et crée les objets dans l’ordre croissant des adresses. Inversement, l’opérateur delete[] détruit les objets du tableau dans l’ordre décroissant des adresses avant de libérer la mémoire. La manière dont les objets sont construits et détruits par les opérateurs new et new[] dépend de leur nature. S’il s’agit de types de base du langage ou de structures simples, aucune initialisation particulière n’est faite. La valeur des objets ainsi créés est donc indéfinie, et il faudra réaliser l’initialisation soi-même. Si, en revanche, les objets créés sont des instances de classes C++, le constructeur de ces classes sera automatiquement appelé lors de leur initialisation. C’est pour cette raison que l’on devra, de manière générale, préférer les opérateurs C++ d’allocation et de désallocation de la mémoire aux fonctions malloc et free du C. Ces opérateurs ont de plus l’avantage de permettre un meilleur contrôle des types de données et d’éviter un transtypage. Les notions de classe et de constructeur seront présentées en détail dans le chapitre traitant de la couche objet du C++.

Un C amélioré Allocation dynamique L’opérateur new[] alloue la mémoire et crée les objets dans l’ordre croissant des adresses. Inversement, l’opérateur delete[] détruit les objets du tableau dans l’ordre décroissant des adresses avant de libérer la mémoire. La manière dont les objets sont construits et détruits par les opérateurs new et new[] dépend de leur nature. S’il s’agit de types de base du langage ou de structures simples, aucune initialisation particulière n’est faite. La valeur des objets ainsi créés est donc indéfinie, et il faudra réaliser l’initialisation soi-même. Si, en revanche, les objets créés sont des instances de classes C++, le constructeur de ces classes sera automatiquement appelé lors de leur initialisation. C’est pour cette raison que l’on devra, de manière générale, préférer les opérateurs C++ d’allocation et de désallocation de la mémoire aux fonctions malloc et free du C. Ces opérateurs ont de plus l’avantage de permettre un meilleur contrôle des types de données et d’éviter un transtypage. Les notions de classe et de constructeur seront présentées en détail dans le chapitre traitant de la couche objet du C++.

Un C amélioré Allocation dynamique Lorsqu’il n’y a pas assez de mémoire disponible, les opérateurs new et new[] peuvent se comporter de deux manières selon l’implémentation. Le comportement le plus répandu est de renvoyer un pointeur nul. Cependant, la norme C++ indique un comportement différent : si l’opérateur new manque de mémoire, il doit appeler un gestionnaire d’erreur. Ce gestionnaire ne prend aucun paramètre et ne renvoie rien. Selon le comportement de ce gestionnaire d’erreur, plusieurs actions peuvent être faites : soit ce gestionnaire peut corriger l’erreur d’allocation et rendre la main à l’opérateur new ( le programme n’est donc pas terminé), qui effectue une nouvelle tentative pour allouer la mémoire demandée ; soit il ne peut rien faire. Dans ce cas, il peut mettre fin à l’exécution du programme ou lancer une exception std::bad_alloc, qui remonte alors jusqu’à la fonction appelant l’opérateur new. C’est le comportement du gestionnaire installé par défaut dans les implémentations conformes à la norme.

Un C amélioré Allocation dynamique L’opérateur new est donc susceptible de lancer une exception std::bad_alloc. Voir la partie qui traite de la gestion des exceptions pour plus de détails à ce sujet. Il est possible de remplacer le gestionnaire d’erreur appelé par l’opérateur new à l’aide de la fonction std::set_new_handler, déclarée dans le fichier d’en-tête new. Cette fonction attend en paramètre un pointeur sur une fonction qui ne prend aucun paramètre et ne renvoie rien. Elle renvoie l’adresse du gestionnaire d’erreur précédent. La fonction std::set_new_handler et la classe std::bad_alloc font partie de la bibliothèque standard C++. Comme leurs noms l’indiquent, ils sont déclarés dans l’espace de nommage std::, qui est réservé pour les fonctions et les classes de la bibliothèque standard. Voyez aussi le Chapitre 11 pour plus de détails sur les espaces de nommages. Si vous ne désirez pas utiliser les mécanismes des espaces de nommage, vous devrez inclure le fichier d’en-tête new.h au lieu de new. Attendez vous à ce qu’un jour, tous les compilateurs C++ lancent une exception en cas de manque de mémoire lors de l’appel à l’opérateur new, car c’est ce qu’impose la norme. Si vous ne désirez pas avoir à gérer les exceptions dans votre programme et continuer à recevoir un pointeur nul en cas de manque de mémoire, vous pouvez fournir un deuxième paramètre de type std::nothrow_t à l’opérateur new. La bibliothèque standard définit l’objet constant std::nothrow à cet usage.

Un C amélioré Allocation dynamique Les opérateurs delete et delete[] peuvent parfaitement être appelés avec un pointeur nul en paramètre. Dans ce cas, ils ne font rien et redonnent la main immédiatement à l’appelant. Il n’est donc pas nécessaire de tester la non nullité des pointeurs sur les objets que l’on désire détruire avant d’appeler les opérateurs delete et delete[].

Un C amélioré Fonctions génériques La générécité est le fait que el code d’une fonction soit indépendant du type des objets qu’elle manipule. template <liste de paramètres> fonction Avec T = int, ça donne inline T add (T a , T b){ return a+b; } Template <class T> inline T add (T a , T b){ return a+b; }

Un C amélioré Fonctions génériques Template <class T> T max (T t[] , int sz){ T mx = t[0]; for(int i = 1; i < sz; i++) mx = max<T>(mx, t[i]); return mx; } Template <class T> inline T max (T x , T y){ return (x>y ? x:y); } int tab_int[] = {2, 4, 8, 16, 1, 5, 9, 17}; float tab_float [] = {1.0, 3.3, 4.4, 2.2, 10.10, 1.3}; cout << max<int> (tab_int, int (sizeof(tab_int)/sizeof(int))); cout << max<float> (tab_float, int (sizeof(tab_float)/sizeof(float)));

Un C amélioré Fonctions génériques On peut aussi appeler explicitement les types du template lors de l’appel de la fonction. max(1,2); est équivalent à Max<int> (1,2);

Un C amélioré Fonctions génériques Exercice. Avec les templates, reinterpret_cast et le passage de paramètres Par référence, écrivez une fonction générique qui recopie octet par octet le contenu d’un opérande dans un autre (une sorte de strcpy() en C++). Solution. template <class TS, class TD> void memcpy(TS src, TD &dst) { int sz = sizeof(src); if( sz > sizeof(dst)) sz = sizeof(dst); char *src2 = reinterpret_cast<char*>(&src); char *dst2 = reinterpret_cast<char*>(&dst); for (int i=0; i < sz; i++) dst[i] = src[i]; }

Un C amélioré Fonctions génériques Exercice. Réutilisez la fonction memcpy() dans une nouvelle fonction tabcpy() dont le but est de copier un tableau dans un autre de même taille. Solution. template <class E> void tabcpy(E *src, E *dst, int n) { for (int i=0; i < n; i++) memcpy<E,E> (src[i], dst[i] ); }

Un C amélioré Les entrées/sorties Le concept de flots d’octets C++ Description C iostream bibliothèque C++ des flots stdio.h istream flot d ‘entrée – ostream flot de sortie - << opération de sortie printf >> opération d ‘entrée scanf cin flot entrée standard stdin cout flot de sortie standard stdout cerr flot de sortie des erreurs stderr clog flot desortie des erreurs - endl fin deligne ” \n ‘

Un C amélioré Les entrées/sorties Le concept de flots d’octets Les opérateurs << et >> sont génériques. On peut mélanger les types. short s = 1; int i = 2; long l = 3; float f= 4.5; double d = 6.7; cout<<"s= "<<s<<"i= "<<i…"d= "<<d<<endl; Il n ‘y a plus de chaîne de format à la « printf».

Un C amélioré Les entrées/sorties Le concept de flots d’octets Les opérateurs << et >> sont génériques. Ils sont définis sur les types: bool, short, int, long float, double char, char* On doit les redéfinir soi-même pour les types que l ‘on crée si on veut les afficher.

Un C amélioré Les entrées/sorties Le concept de flots d’octets On dispose de manipulateurs spéciaux sur les flots C++ C syntaxe fait quoi flush fflush cout << flush; vide le tampon endl \n + fflush cout << endl; fin de ligne hex - cout << hex; hexa oct - cout << oct; octal dec - cout << dec; décimal ws - cin >> ws; élimine blancs

Un C amélioré Les entrées/sorties Le concept de flots d’octets Il existe des manipulateurs ayant des paramètres, ils sont Définis dans la bibliothèque « iomanip » #include <iomanip> setw(n) en sortie spécifie la largeur du champ en caractères cadrage à droite : int i = 777; cout << setw(10) << i; setw(n) en entrée spécifie le nombre max de caractères lus char t[10] cin << setw(10) << t;

Un C amélioré Les entrées/sorties Le concept de flots d’octets #include <iomanip> setprecision(n) spécifie le nombre de chiffres significatifs pour les nombres flottants. Par défaut, le nombre de chiffres et 6 (partie entière + décimale). float f= 12345.6789; cout << f<< endl; cout << setprecision(9) << f<< endl;

Un C amélioré Les entrées/sorties Le concept de flots d’octets #include <iomanip> setfill(” caractère‘) spécifie le caractère de remplissage pour setw(n). Par défaut c‘est un espace « blanc» int i= 777; cout << setfill(” . ‘); cout << setw(10) << i;

Un C amélioré Les entrées/sorties Le concept de flots d’octets L ‘opérateur >> est évalué à vrai tant que la fin de fichier n ‘est pas atteinte. Voici « cat » (avec un seul fichier) en C++ int main() { char c; while(cin >> c) cout << c; return 0; }

Un C amélioré Les entrées/sorties Le concept de flots d’octets Si vous consultez le résultat, vous devriez avoir une surprise. La recopies s‘est faite sans les séparateurs (blanc, tab, return, line feed, …). Essayez le programme suivant. int main() { char c; while(cin.get(c)) cout << c; return 0; }

La fonction symétrique de get() est put() Un C amélioré Les entrées/sorties Le concept de flots d’octets La fonction symétrique de get() est put() int main() { char c; while(cin.get(c)) cout.put(c); return 0; }

Un C amélioré Les entrées/sorties Le concept de flots d’octets La fonction getline() permet de lire une ligne complète, séparateurs compris. const int MAX = 10; int main() { char t[MAX]; while(cin.getline(t, MAX, ‘\n‘)) cout << t << endl; return 0; }

Un C amélioré Les entrées/sorties #include <iostream> int const MAX = 10; // eof+bad+good+fail+clear test int main() { char line[MAX]; for (;;){ cin.getline(line, MAX, '\n'); if (cin.fail()) cout << " fail "; if (cin.bad()) cout << " bad "; if (cin.good()) cout << " good "; if (cin.eof()) break; if (!cin.good()) cin.clear(); cout << line << endl; } cout << "eof" << endl; return 0; Le concept de flots d’octets Fonctions de flots eof() fin de fichier fail() erreur bad() problème grave good() pas de problème clear() annule les problèmes

Un C amélioré Les entrées/sorties Autres fonctions de flots putback(char) « pousse» un octet qui sera lu par get() peek() lit l ‘octet sans le retirer du flot seekg() positionne le pointeur de get() seekp() positionnelepointeur de put() tellg() retourne le pointeur de get() tellp() retourne le pointeur de put()

Un C amélioré Les entrées/sorties On peut lire les flots dans un fichier open() close() Il y a de nouvelles fonctions de flots #include <ifstream> #include <ofstream> #include <fstream> Il y a de nouveaux includes

flot.open("toto", ios::out|ios::trunc); f= fopen("toto", "w"); Un C amélioré Les entrées/sorties Flots et fichiers : fopen() modes C++ signification ios::in read ios::write write ios::trunc tronque ios::app append ios::binary binaire En C flot.open("toto", ios::out|ios::trunc); f= fopen("toto", "w");

Un C amélioré Les entrées/sorties Flots et fichiers : fopen() Un fichier est automatiquement ouvert à la création d ‘une variable si on utilise la syntaxe suivante: ofstream ofs ("toto.out", ios::out|ios::trunc); ifstream ifs ("toto.in", ios::in);

Un C amélioré Les entrées/sorties Flots et fichiers : fclose() flot.fclose(); C++ est « dynamique»: lorsqu‘une variable de flot n ‘existe plus, le fichier éventuellement attaché au flot est automatiquement fermé.

Un C amélioré Les entrées/sorties Différence entre le C et le C++ - commentaires // -nouveaux mots réservés -les caractères sont vraiment des char, plus des int -une chaîne de caractères est un tableau de « const char » -la structure définit aussi le type de même nom -un idf const et pas extern ne peut être utilisé ailleurs -on ne peut convertir un void* vers un pointeur sans cast -seuls les pointeurs sur des non const et non volatile peuvent être convertis implicitement vers des void* -déclaration implicite de fonctions est interdite -fonctions avec type de retour nécessite return -extern et static sont réserves à des objets ou des fonctions -toute constante doit être initialisée -pas de type implicite int -on ne mélange pas les enum entre eux ni avec des int

Nouveaux types Types abstraits Concept de classe Constructeur -destructeur Imbrication de classes Données membres de type classe « friend » Ordred ‘appel des const/destr Classes génériques Surdéfinition d ‘opérateurs Données membres statiques Fonctions membres statiques Opérateur de portée Conversion de types Portée des identificateurs Espaces de nommage Constantes « non constantes »

Nouveaux types Types abstraits Un type abstrait de données est un ensemble de valeurs ou un ensemble d ‘objets (informations organisées) ainsi que les opérations permises sur ces valeurs. Exemple. Les entiers munis des quatre opérations arithmétiques. L ‘utilisateur ne connaît pas la représentation interne. La fonctionnalité est plus importante que la représentation.

Nouveaux types Concept de classe Une classe est un ensemble de: données ayant un lien entre elles (comme struct) « données membres » + méthodes servant à manipuler les données membres « fonctions membres » C’est la structure de base des langages orientés objet.

Nouveaux types Concept de classe Une classe est un ensemble de données et de méthodes privées (partie invisible, masquée) ou public (partie visible, accessible à l’utilisateur)

Nouveaux types Concept de classe Les méthodes sont de quatre types : les constructeurs (initialisation correcte) destructeur (fin de vie correcte) les accesseurs (lecture seulement) les modificateurs (lecture+écriture)

Nouveaux types Concept de classe Syntaxe de la définition d’une classe class <class name> {<data + functions>} <vars> ; La liste de données et de fonctions est marquée par les mots clés « public» et « private» qui dénotent respectivement quelles sont les données et les fonctions « publiques » ou« privées ». Par défaut, en l ‘absence de ces mots clés, les données et les fonctions sont « privées ».

Nouveaux types Concept de classe constructeur class FigureGeometrique { public: FigureGeometrique(); ~FigureGeometrique(); inline double surface() const; inline const char *nom() const; void surface (double); private: const char * nom; double surface; } destructeur accesseurs modificateur

Nouveaux types Concept de classe Les fonctions « inline» doivent être définies dans le même fichier que celui dans lequel est déclarée la classe. Par défaut, toute fonction définie dans le corps de la classe est automatiquement « inline» (définition dans FigureGeometrique.h). Les fonctions définies en dehors de la classe doivent être préfixées par <class name>:: Elles peuvent être définies dans un autre fichier que celui de la Classe (définition dans FigureGeometrique.cpp): double FigureGeometrique::surface() const { return surface; } const char * FigureGeometrique::nom() const { return nom; void FigureGeometrique::surface(double s) { surface= s;

Nouveaux types Concept de classe Exemple de fonctions définies à l‘intérieur du contexte de la déclaration dans le fichier FigureGeometrique.h. class FigureGeometrique { public: FigureGeometrique(); ~ FigureGeometrique(); inline double surface() const { return surface; } inline const char * nom() const { return nom; } void surface(double s) { surface= s; } private: const char *nom; double surface; };

Nouveaux types Concept de classe Les objets sont les instances de classes. FigureGeometriqueX; // déclaration X.surface(2.0); // écriture double d = X.surface(); // lecture cout << d << endl; // affichage

Nouveaux types Constructeurs class X { public : X(); … }; - portent le même nom que la classe n ‘ont pas de type de retour sont invoqués lors de la création d‘un objet déclaration d‘une variable variable temporaire (compilateur) usage de new ou new [] - diffèrent par leur signature(forcément :-)

Nouveaux types Constructeurs Il peut y avoir plusieurs constructeurs. class X { public: X() X(const X&); X& operator = (const X&); … }; Constructeur par défaut Constructeur de copie Surdéfinition de = X a; //défaut X b = a; // copie X c(a); //copie X d = X(a); //double copie d = a //affectation

Nouveaux types Constructeurs La double copie, qu’est ce que c’est ? X d = X(a); //double copie 1/ création d ‘un objet temporaire qui est une copie de a => X(a) 2/ recopie de l ‘objet temporaire dans le nouvel objet d 3/ libération (appel au destructeur) de l ‘objet temporaire X(a)

Nouveaux types Constructeurs Si le programmeur ne veut pas de constructeurs, le compilateur le fait automatiquement pour lui, cependant, il faut les spécifier à vide lors de la déclaration de la classe. class X { public: X() {} X(const X&) {} X& operator=(const X&) {} } ;

Nouveaux types Constructeurs Pourquoi 3 constructeurs ? Défaut = initialisation Copie = recopie à partie d’un modèle Affectation = doit aussi traite le cas « X = X; » Notation fonctionnelle à la « constructeur de copie » int i(0) est équivalent à int i = 0; i(2) i = 2;

Nouveaux types Constructeurs Pour désigner l’objet en cours de construction, il y a la variable « this » qui est un pointeur sur un objet de la classe du constructeur. X& X::operator=(const X& arg) { if(this == &arg) return *this; … return *this; }

Nouveaux types Destructeur class X { public : ~X(); … }; - porte le même nom que la classe avec ~ n ‘a pas de type de retour sont invoqués lors de la destruction d‘un objet fin de portée de la déclaration d‘une variable fin de la variable temporaire (compilateur) usage de delete ou delete [] - n’a jamais d’argument, il n’y en as donc qu’un

Nouveaux types Destructeur Si le programmeur ne définit pas le destructeur, le compilateur le fait automatiquement pour lui, cependant, et comme les constructeurs, ile faut le spécifier à « vide » lors de la déclaration de la classe. class X { public: ~X() {} … } ;

Nouveaux types Classe orthodoxe et canonique La classe minimale correcte doit contenir les méthodes suivantes: class X { public: X() {...}; X(const X&) {...}; X& operator=(const X&) {...}; ~X() {...}; } ;

Nouveaux types Un « cauchemar » avec les const class X { public: const int * const int_ptr(const int) const; }; const int * const X::int_ptr(const int i) const { static const int ii = 10; static const int * const ptr = &ii; return ptr; } const X x; const int i = 20; cout << *(x.int_ptr(i)) << endl;

Nouveaux types Imbrication de classes On peut déclarer une classe dans le corps d’une autre classe class X { public: X(); … private: class Y {...}; ... };

Nouveaux types Données membres Les données membres d’une classe peuvent être des objets, c’est-à-dire des instances d’une autre classe. class Y {...}; class X { public: X(); … private: Y y; ... };

Nouveaux types « friend » Une classe peut être « amie » d’une autre classe et avoir accès à ses données et méthodes privées. class B{ public: B() {a = new A();}; ~B() {}; private: A *a; void reinit_a() { a->i = 0; a->f= 0.0; }; class A{ public: friend class B; A() {i = 0; f = 0.0;}; ~A() {}; private: int i; float f; };

Nouveaux types Ordre d’Appel des constructeurs et destructeur Les constructeurs des données membres sont appelés avant le constructeur De la classe qui les englobe et dans l ‘ordre de déclaration des données. Les destructeurs des données membres sont appelés après le destructeur de la classe qui les englobe et dans l ‘ordre inverse de déclaration des données.

Nouveaux types Ordre d’Appel des constructeurs et destructeur Exercice class X{ public : X() { cout << ‘x’;} ~X() {cout << ‘X’;} class Y y; class Z z; }; class Z{ public : Z() { cout << ‘z’;} ~Z() {cout << ‘Z’;} }; int main() { X x; cout << endl; return 0; }; class Y{ public : Y() { cout << ‘y’;} ~Y() {cout << ‘Y’;} };

Nouveaux types Classes génériques Une classe générique est une classe avec un template. Même principe que les fonctions génériques. Template <class T> class Nb{ public : T a, b; T add (){ return a+b}; }; Nb<int> N; N.a = 1; N.b = 2; cout << N.Add() << endl;

Nouveaux types Surcharges des opérateurs Les opérateurs que l’on peut surcharger sont : + - * / % ^ & | ~ ! = < > <= >= ++ -- << >> == != && || += -= /= *= ^= &= |= <<= >>= [] () -> new delete new[] delete[] Les opérateurs quel ‘on nepeut pas surcharger sont: :: . ?: sizeof

Nouveaux types Surcharges des opérateurs On ne peut changer les règles de précédence l’associativité le nombre d’opérandes On ne peut créer de nouveaux opérateurs

Nouveaux types Surcharges des opérateurs template<class T> class X { public: X& operator+(const X& arg){ X *x = new X(); x->n = n + arg.n; return *x; } T n; }; Exercice: utilisez cette classe pour écrire un programme qui crée un nouvel objet C par « addition » de deux objets A et B.

Nouveaux types Surcharges des opérateurs Les opérateurs de flot << et >> peuvent aussi être redéfinis mais comme ils sont définis dans les classes « stream » ils ne peuvent être redéfinis dans une nouvelle classe… Solution = fonction/méthode« friend » class A { friend ostream& operator <<(ostream&, const A&); friend istream& operator >>(istream&, const A&); public: A() {i=0; f=0.0;}; ~A() {}; private: int i; float f; };

Nouveaux types Surcharges des opérateurs ostream& operator<<(ostream& o, const A& a) { o << "i = "<< a.i << " f= "<< a.f<< endl; return 0; } istream& iperator<<(istream& i, const A& a) i >> a.i >> a.f; return i;

Nouveaux types Surcharges des opérateurs Exercice Écrivez un programme dans lequel une classe contenant au moins deux chaînes de caractères « nom » et « prénom » avec des données privées et des méthodes publiques permettra de lire, écrire, saisir et afficher ces données en redéfinissant les opérateurs de flots.

Nouveaux types Surcharges des opérateurs Problèmes des opérateurs ++ et --. Ils peuvent être en préfixe ou en suffixe. Lequel est redéfini ? class X { ... X& operator++();   //préfixe X& operator++(int); //suffixe  …   };  

Nouveaux types Surcharges des opérateurs Exercice Écrivez une classe contenant au moins une donnée entière et dans laquelle les deux incrémentations et les deux décrémentations sont définies mais font +2et -2 en préfixé et +3 et -3 en infixé.

Nouveaux types Données et membres statiques Sorte de variable globale « locale à la classe » initialisée à 0 par le compilateur si pas initialisée par le programmeur class A { friend class B; public: A() { i =0; f = 0.0; n++;}; ~A() {}; static int n; private : int i; float f; } int A:: n =-1;

Nouveaux types Données et membres statiques Fonction/méthode «de classe » qui seule peut modifier les données membres statiques class A { friend class B; public: A() { i =0; f = 0.0; n++;}; ~A() {}; static void reset { n = 0;} private: int i; float f; static int n; } A:: reset();

Nouveaux types Fonctions et données statiques Exercice Grâce aux fonctions et aux données statiques, écrire une classe qui compte en permanence le nombre d’objets « actifs », le nombre d’objets « créés » et le nombre d’objets détruits. Ajoutez une fonction d’affichage de ces trois nombres par redéfinition de l’opérateur de sortie sur un flot.

Nouveaux types Opérateur de portée :: L’opérateur de portée « :: » permet d’Accéder aux données et aux fonctions statiques des classes, mais aussi aux variables globales. int X:: i; int Y:: i; int i; const int I = 2; class X { public: static int i; static const int I = 1; }; class Y { static const int I = 3; int main(){ int i; const int I =4; i = I; ::i = ::I; X::i = X::I; Y::i = Y::I; cout << i << ::i; cout << X::i << Y::i; cout << endl; return 0; }

Nouveaux types Conversion de type Il y a une conversion implicite vers un objet d’une classe s’il existe un constructeur ayant pour paramètre la valeur à convertir. class X { public: X() { n = 0;}; X(int i) { n = i;}; private: int n; }; X x = 0; 0 est converti en un objet X(0) car le constructeur X(int) existe.

Nouveaux types Conversion de type Il y a une conversion implicite vers un objet d’une classe s’il existe un constructeur ayant pour paramètre la valeur à convertir. class X { public: X() { n = 0;}; X(int i) { n = i;}; private: int n; }; X x (0); 0 est converti en un objet X(0) car le constructeur X(int) existe.

Nouveaux types Conversion de type Il y a une conversion implicite vers un objet d’une classe s’il existe un constructeur ayant pour paramètre la valeur à convertir. class X { public: X() { n = 0;}; X(int i) { n = i;}; private: int n; }; X x (0); 0 est converti explicitement En en un objet X(0) avant recopie dans x.

Nouveaux types Conversion de type C++ permet de définir ses propres fonctions de conversion vers d’autres types prédéfinis, ou d’autres classes. Syntaxe: operator<type> () class X { public: X() { n = 0;}; X(int i) { n = i;}; operator int () const {return n;}; private: int n; }; X x = 10; int b = a; int c = int(a);

Nouveaux types Conversion de type Exercice Écrivez un programme dans lequel une classe Entier représente les entiers et permet de mélanger les objets instance de la classe Entier avec les véritables entiers dans les expressions arithmétiques contenant les quatre opérations de base. Ajoutez aussi les opérateurs de flots << et >>.

Portée de l’espace de nommage Nouveaux types Portée des identificateurs Il y en a 5! Portée globale Portée de fichier Portée de bloc Portée de classe Portée de l’espace de nommage

Nouveaux types Portée des identificateurs Portée globale L’allocation de mémoire est faite statiquement à la compilation, initialisation à 0 (comme toutes les variables globales non initialisées en C). … int N; int a =N + 1; N est connu dans toute l’application, les autres Fichiers doivent la déclarer « extern » pour Pouvoir y accéder sans la dupliquer.

Nouveaux types Portée des identificateurs Portée de fichier L’allocation de mémoire est faite statiquement à la compilation, initialisation à 0 (comme toutes les variables globales non initialisées en C). … static int N; int a =N + 1; N est connu dans le fichier seulement, les autres fichiers ne peuvent y accéder, même s’ils déclarent une autre variable N ou tente de la qualifier « extern ».

Nouveaux types Portée des identificateurs Portée de bloc L’allocation de mémoire est faite dynamiquement dans la pile. { … int N; } N n’est connu que dans cette zone. C’est-à-dire après sa déclaration et jusqu’à La fermeture du bloc englobant.

Nouveaux types Portée des identificateurs Portée de bloc L’allocation de mémoire est faite statiquement à la compilation, initialisation à 0 (comme toutes les variables globales non initialisées en C). { … static int N; } N n’est connu que dans cette zone. C’est-à-dire après sa déclaration et jusqu’à La fermeture du bloc englobant.

Nouveaux types Portée des identificateurs Portée de classe Un identificateur de membre d’une classe ne peut être utilisé que: - Dans une fonction membre de la classe Après l’opérateur « . » appliqué à un objet instance de la classe Après l’opérateur « -> » appliqué à un pointeur sur un objet instance de la classe Après l’opérateur de résolution de portée appliqué à la classe

Dans une fonction membre de la classe Nouveaux types Portée des identificateurs Portée de classe Dans une fonction membre de la classe class X { public: int a() { return b() + c();} private: int b() {…}; int c() {…}; int d() { return 2* a();} };

Après l’opérateur « . » appliqué à un objet instance de la classe Nouveaux types Portée des identificateurs Portée de classe Après l’opérateur « . » appliqué à un objet instance de la classe class X { public: int a() { return b() + c();} private: int b() {…}; int c() {…}; int d() { return 2* a();} }; X x; cout << x.a() << endl;

Nouveaux types Portée des identificateurs Portée de classe Après l’opérateur « ->. » appliqué à un pointeur sur un objet instance de la classe class X { public: int a() { return b() + c();} private: int b() {…}; int c() {…}; int d() { return 2* a();} }; X *x = new X(); cout << x->a() << endl;

Après l’opérateur de résolution de portée appliqué à la classe Nouveaux types Portée des identificateurs Portée de classe Après l’opérateur de résolution de portée appliqué à la classe class X { public: static void A() {…}; int a() { return b() + c();} private: int b() {…}; int c() {…}; int d() { return 2* a();} }; X:: A();

Portée d’espace de nommage Nouveaux types Portée des identificateurs Portée d’espace de nommage Pourquoi ? Lorsque l’on se linke avec une Bibliothèque on réutilise les idfs Qui y sont définis. On ne peut pas Définir d’idf ayant le même nom Que l’un quelconque des idfs de la Bibliothèque dans son programme. Il y aurait une « double définition », Fatale pour le linker… namespace <idf> { <declarations> }

Portée d’espace de nommage Nouveaux types Portée des identificateurs Portée d’espace de nommage Comment ? Chaque fournisseur de bibliothèque Doit encapsuler ses déclarations dans Un espace de nommage. Les STL, les librairies standards du C++, Sont dans l’espace de nommage « std ». namespace <idf> { <declarations> }

Portée d’espace de nommage Nouveaux types Portée des identificateurs Portée d’espace de nommage Espace de nommage « anonyme » est équivalent à l’usage de « static » mais sans avoir besoin de « static » devant les déclarations. namespace { <declarations> } En effet, le standard veut que tous les symboles déclarés dans un espace de Nommage anonyme aient comme portée le fichier seulement.

Portée d’espace de nommage Nouveaux types Portée des identificateurs Portée d’espace de nommage Pour faire référence à un idf d’un espace de nommage : deux situation. Qualification <nom de l’espace>:: idf « using » using namespace <nom de l’espace>

Portée d’espace de nommage Nouveaux types Portée des identificateurs Portée d’espace de nommage Il est possible d’imbriquer des espaces de nommage. Il est également possible de créer des alias. namespace toto { <declarations> namespace tutu {…} } namespace titi = toto::tutu;

Nouveaux types Constantes non constantes Ce n’est pas une blague de mauvais goût! Ce sont des membres dits « mutables » Mutable n’est pas compatible avec const et static Mutable signifie « ne sera jamais constant » Permet au programmeur de changer la valeur d’une donnée membre même Si l’objet dans lequel elle se trouve est déclaré comme constant! class X { public: X(int a =4) {i = a;}; int lireI() const { return i++;}; private: mutable int i; }; const X x; cout << x.lireI() << endl;

Programmation orienté objet Avantages de la programmation par objet Héritage Persistance Concept objet avancé en C++: Objets homogènes Classes abstraites Structures hétérogènes Héritage multiple Gestion des exception

Programmation orienté objet Modularité Encapsulation Abstraction Extensibilité 1 classe = 1 module Parties privée et publique des classes Interface de la classe = type abstrait + généricité (templates) Héritage

Programmation orienté objet class X: <mode> Y { … } Syntaxe Mode Ancètre Dérivée Private Private Inaccessible Protected Private Public Private Protected Private Inaccessible Protected Protected Public Protected Public Private Inaccessible Protected Protected Public Public public protected private Mode

Programmation orienté objet Héritage « public » class Y : public X { public: Y() { x1 = 0; //public x2 = 0; //protected x3 = 0; //NON } }; Y y; Cout << y.x1 << endl; Cout << y.x2 << endl; //NON Cout << y.x3 << endl; //NON class X{ public: int x1; protected: int x2; private: int x3; }; X x; cout << x.x1 << endl; Cout << x.x2 << endl; //NON Cout << x.x3 << endl; //NON

Programmation orienté objet Héritage « protected » class X{ public: int x1; protected: int x2; private: int x3; }; class Y : protected X { public: Y() { x1 = 0; //protected x2 = 0; //protected x3 = 0; //NON } }; Y y; Cout << y.x1 << endl; //NON Cout << y.x2 << endl; //NON Cout << y.x3 << endl; //NON X x; cout << x.x1 << endl; Cout << x.x2 << endl; //NON Cout << x.x3 << endl; //NON

Programmation orienté objet Héritage « private » class X{ public: int x1; protected: int x2; private: int x3; }; class Y : private X { public: Y() { x1 = 0; //private x2 = 0; //private x3 = 0; //NON } }; Y y; Cout << y.x1 << endl; //NON Cout << y.x2 << endl; //NON Cout << y.x3 << endl; //NON X x; cout << x.x1 << endl; Cout << x.x2 << endl; //NON Cout << x.x3 << endl; //NON

Programmation orienté objet Construction des objets Nouvelles données membres Données membres classe ancêtre Un objet d’une classe dérivée contient Redéfinition d’anciennes données membres

Programmation orienté objet Contenu de la classe dérivée Fonctions membres classe ancêtre Nouvelles +redéfinition fonctions membres Pas les « friend » Une classe dérivée contient Nouveaux constructeurs Nouveau destructeur

Programmation orienté objet Construction des objets class X{ public: int x1; void a() {…} protected: int x2; private: int x3; }; class Y : public X { public: int x1; //redéfini int y; //nouveau void a() {…} // redéfini void b() {…} // nouveau int a() {…} // nouveau };

Programmation orienté objet Construction des objets Si on veut utiliser une fonction de la classe ancêtre alors qu’elle a été redéfinie dans la classe dérivée, il faut utiliser l’opérateur de portée « :: » class X{ public: void a() {…} }; class Y : public X { public: void a() {…} // redéfini void b() { X::a();// autre classe a(); //local } };

Programmation orienté objet Construction des objets Lors de la construction d’un objet des constructeurs de toute la hiérarchie Des classes sont appelées dans l’ordre de la hiérarchie « top-down ». class Z : public Y { public: Z() { cout << ‘z’;} }; class X{ public: X() {cout << ‘x’;} }; int main() { Z z; cout << endl; } class Y : public X { public: Y() { cout << ‘y’;} };

Programmation orienté objet Construction des objets S’il n’y a pas de constructeur par défaut spécifié par le programmeur, Il faut alors une séquence d’initialisation!!! class Y : public X { public: int j; Y(int n): X(n) { j = n;} }; class X{ public: int i; X(int n) { i = n;} }; class Z : public Y { public: int k; Z(int n): Y(n) { k = n;} };

Programmation orienté objet Destruction des objets Lors de la destruction d’un objet, les destructeurs de toute la hiérarchie des classes sont appelées dans L’ordre de la hiérarchie « bottom-up » class Z : public Y { public: ~Z() {cout << ‘Z’;} }; class X{ public: ~X() { cout << ‘X’;} }; int main() { Z z; cout << endl; } class Y : public X { public: ~Y() {cout << ‘Y’;} };

Programmation orienté objet Liaison dynamique C’est la possibilité de ne lier qu’au moment de l’exécution la méthode à invoquer (pas possible pour les constructeurs). class X{ public: virtual void a() { < cout << ‘X’ << endl;} }; X x; Y y; x.a(); y.a(); x = y; x.a();//? X *x = new X(); Y *y = new Y(); X->a(); Y->a(); x = y; X->a();//? class Y : public X { public: virtual void a() { cout << ‘Y’ << endl;} };

Programmation orienté objet Liaison dynamique C’est la possibilité de ne lier qu’au moment de l’exécution la méthode à invoquer (pas possible pour les constructeurs). class X{ public: virtual void a() { < cout << ‘X’ << endl;} }; X x; Y y; x.a(); y.a(); x = y; x.a();//? X *x = new X(); Y *y = new Y(); X->a(); Y->a(); x = y; X->a();//? class Y : public X { public: virtual void a() { cout << ‘Y’ << endl;} };

Programmation orienté objet Liaison dynamique Que se passe-t-il sans « virtual » ? class X{ public: void a() { < cout << ‘X’ << endl;} }; X *x = new X(); Y *y = new Y(); X->a(); Y->a(); x = y; X->a();//? class Y : public X { public: void a() { cout << ‘Y’ << endl;} };

Programmation orienté objet Classe abstraite C’est une classe « mère » dans laquelle au moins une méthode est virtuelle et pure. Virtuelle = utilise le mot clé « virtual » Pure = pas d’implémentation de la méthode + «  = 0 » C’est similaire au concept d’interface en Java. Attention: il n’est pas permis de créer d’objet instances d’une classe abstraire. Inutile de spécifier les constructeurs et le destructeur.

Programmation orienté objet Classe abstraite Classe abstraire class X{ public: virtual void a() = 0; virtual void b() = 0; … }; Classe non abstraire virtual est facultatif class Y : public X { public: virtual void a() { cout << ‘a’ << endl;} virtual void b() { cout << ‘b’ << endl;} };

Programmation orienté objet Héritage multiple class X{ public: int i; X(int n) { i = n;} void a() {…}; }; class T: public X, public Y, public Z { public: int i; //redéfinition de i!! T(): X(0), Y(0), Z(0) { i =0;} void c() { j +=1; i +=1; b(); //pas d’ambiguité //a(); //ambiguité X::a(); Y::a(); } }; class Y { public: int i; Y(int n) { i=n;} void a() {…} }; class Z { public: int j; Z(int n) { j=n;} void b() {…} };

Programmation orienté objet Héritage multiple class X{ public: int i; X(int n) { i = n;} void a() {…}; }; class T: public Y, public Z { public: int l; T(): X(0), Y(0), Z(0) { l = 0;} void d() { j +=1; k +=1; l +=1; //i +=1; //ambiguité Y::i +=1; Z::i +=1; //a(); //ambiguité Y::a(); Z::a(); } }; class Y: public X { public: int j; Y(int n) { j=n;} void b() {…} }; class Z: public X { public: int k; Z(int n) { k=n;} void c() {…} };

Programmation orienté objet Héritage multiple class X{ public: int i; X(int n) { i = n;} void a() {…}; }; class T: virtual public Y, virtual public Z { public: int l; T(): X(0), Y(0), Z(0) { l = 0;} void d() { j +=1; k +=1; l +=1; i +=1; a(); } }; class Y: virtual public X { public: int j; Y(int n) { j=n;} void b() {…} }; class Z: virtual public X { public: int k; Z(int n) { k=n;} void c() {…} };

Programmation orienté objet Les exceptions Une fonction en face d’un problème qu’elle ne sait pas résoudre lance (throw) Une exception en espérant qu’une fonction appelante à un niveau supérieur Puisse l’intercepter (catch). Cas archi-classique de la division par 0 : le problème. class X{ public: int i; X(int n) { i = n;} void div(int n) {i /=n;} };

Programmation orienté objet Les exceptions Cas archi-classique de la division par 0 : la solution en C++. class X{ public: int i; X(int n) { i = n;} void div(int n) { if (n==0) throw Div0(); else i /=n; } private: }; class Div0{};

Programmation orienté objet Les exceptions Cas archi-classique de la division par 0 : sa mise en œuvre. int main{ X x(1); try { x.div(0); cout << "Ok" << endl; } catch(Div0){ cout << "Erreur" << endl; return 0; };

Programmation orienté objet Les exceptions Une classe peut renvoyer plusieurs exceptions. class X{ public: int i; X(int n) { i = n;} void div(int n) { if (n==0) throw E1(); else (if n<0) throw E2(); else i /=n; } private: }; Class E1 {}; Class E2 {}; // ou Enum {E1, E2} Exception;

Programmation orienté objet Les exceptions Cas archi-classique de la division par 0 : sa mise en œuvre. int main{ X x(1); try { x.div(0); // x.div(-1); cout <<   "Ok" << endl; }catch(E1){ cout << "Erreur E1" << endl; }catch(E2){ cout <<  "Erreur E2" << endl; } return 0; };