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

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

Embed Size (px)

Citation preview

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 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’argumentsTypes 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++

Un C amélioré

Les commentaires

Deux types de commentaires …

type« bloc»

type« ligne»

/* blablabla 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

double add(double a, double b){ return a + b; } int add(int a, int b){

return a + b; }

int i = add(1, 2);double d = add(1.0, 2.0);

(int, add, int, int)(double, add, double, double)

Il est permis de définir dans un même programme deux ou plusieursfonctions portant le même nom. Cela implique que ces fonctions doiventDifférer au niveau de leur prototype/signature

Un C amélioré

Arguments par défaut

double add(double a, double b = 2.0){ return a + b; }

double d = add(1.0, 2.0);double dd = add(1.0);

Les arguments d’une fonction peuvent avoir des valeurs par défaut pourles paramètres formels de fin. Si, lors d’un appel, les paramètrescorrespondants ne sont pas spécifiés, alors les valeurs par défaut sont utilisées.

Un C amélioré

Fonctions « en ligne »

inline double add(double a, double b){return a + b;

}

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 lesparamètres formels sont remplacés par les paramètres effectifs.

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 leretour de fonction.

Un C amélioré

Passage d’arguments par référence

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);cout << SUM << endl;

En C++, on peut passer par référence sans avoir à manipuler l’adresseexplicitement en utilisant un synonyme d’une variable.

Un C amélioré

Conversion de types

Les conversions de type sont

explicites

ou implicites

Le programmeur « décide »

Le compilateur « décide »

Un C amélioré

Conversion de types

int i;long l;l = (long) i;

Les conversions explicites

(type) expressionComme en C

a) int i; long l; l = long(i);

(type) expressionnotation fonctionnelle

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 explicitesLe « 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; // NONchar *c = const_cast<char *>a; // OK

Un C amélioré

Conversion de types

Les conversions explicitesLe « haut de gamme » de la conversion …

reinterpret_cast<T>(e)

Utilisé pour réinterpréter complètement un type: ce n’estPas 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 explicitesLe « 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 seraientMasqués par des identificateurs locaux.

int i;int f(){ int i; 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’outillagepermettant d’interfacer simplement du C++ avec du C.

extern "C" { int add_int_C(int, int); float add_float_C(float, float);}

int i = add_int_C(1, 2);float f = add_float_C(1.0, 2.0);

Le code de add_int_C et add_float_C se trouvedans un fichier C.

Header C++

Fichier C++

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 pourchacune 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 constructeurde 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 constructeurseront 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 constructeurde 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 constructeurseront 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[] peuventse 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èquestandard C++. Comme leurs noms l’indiquent, ils sont déclarés dans l’espace de nommagestd::, qui est réservé pour les fonctions et les classes de la bibliothèque standard. Voyezaussi le Chapitre 11 pour plus de détails sur les espaces de nommages. Si vous ne désirez pasutiliser les mécanismes des espaces de nommage, vous devrez inclure le fichier d’en-tête new.hau 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 desobjets qu’elle manipule. template <liste de paramètres> fonction

Template <class T>inline T add (T a , T b){

return a+b;}

Avec T = int, ça donneinline T add (T a , T b){

return a+b;}

Un C amélioré

Fonctions génériques

Template <class T>inline T max (T x , T y){ return (x>y ? x:y);}

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;}

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

max(1,2);

est équivalent à

Max<int> (1,2);

On peut aussi appeler explicitement les types du template lors de l’appelde la fonction.

Un C amélioré

Fonctions génériques

Exercice. Avec les templates, reinterpret_cast et le passage de paramètresPar référence, écrivez une fonction générique qui recopie octet par octetle 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

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 ‘

C++ Description C

Le concept de flots d’octets

Un C amélioré

Les entrées/sorties

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».

Le concept de flots d’octets

Un C amélioré

Les entrées/sorties

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.

Le concept de flots d’octets

Un C amélioré

Les entrées/sorties

On dispose de manipulateurs spéciaux sur les flots

Le concept de flots d’octets

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

Il existe des manipulateurs ayant des paramètres, ils sont Définis dans la bibliothèque « iomanip »

Le concept de flots d’octets

#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 pourles 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; }

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

Le concept de flots d’octets

Fonctions de flots

eof() fin de fichierfail() erreurbad() problème gravegood() pas de problèmeclear() annule les problèmes

#include <iostream>int const MAX = 10;// eof+bad+good+fail+clear testint 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;

}

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

Il y a de nouvelles fonctions de flots open() close()

Il y a de nouveaux includes #include <ifstream>

#include <ofstream> #include <fstream>

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

flot.open("toto", ios::out|ios::trunc); f= fopen("toto", "w");

En C

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

class FigureGeometrique {public:

FigureGeometrique();~FigureGeometrique();inline double surface() const;inline const char *nom() const;void surface (double);

private:const char * nom;double surface;

}

constructeur

destructeur

accesseurs

modificateur

Nouveaux types

Concept de classeLes 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 dela 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;

};

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

Nouveaux types

Constructeurs

- 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 :-)

class X {

public : X();…

};

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éfautX b = a; // copieX c(a); //copieX d = X(a); //double copied = 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 = initialisationCopie = recopie à partie d’un modèleAffectation = 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

- 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

class X {

public : ~X();…

};

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

class X { public: X();

private: class Y {...}; ...

};

On peut déclarer une classe dans le corps d’une autre classe

Nouveaux types

Données membres

class Y {...}; class X { public: X();

…private: Y y;

... };

Les données membres d’une classe peuvent être des objets, c’est-à-diredes instances d’une autre classe.

Nouveaux types

« friend »

class B{ public: B() {a = new A();};

~B() {};

private:A *a; void reinit_a() {

a->i = 0; a->f= 0.0;

}; };

Une classe peut être « amie » d’une autre classe et avoir accès à ses données et méthodes privées.

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 Y{public :

Y() { cout << ‘y’;}~Y() {cout << ‘Y’;}

};

class Z{public :

Z() { cout << ‘z’;}~Z() {cout << ‘Z’;}

};

int main(){

X x;cout << endl;return 0;

};

Nouveaux types

Classes génériques

Une classe générique est une classe avec un template. Même principe queles 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édencel’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

Écrivez un programme dans lequel une classe contenant au moins deux chaînesde 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éesen redéfinissant les opérateurs de flots.

Exercice

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

É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é.

Exercice

Nouveaux types

Données et membres statiques

class A {friend class B;

public: A() { i =0; f = 0.0; n++;};~A() {};static int n;

private :int i;float f;

}

Sorte de variable globale « locale à la classe »initialisée à 0 par le compilateur si pas initialisée par le programmeur

int A:: n =-1;

Nouveaux types

Données et 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;

}

Fonction/méthode «de classe » qui seulepeut modifier les donnéesmembres statiques

A:: reset();

Nouveaux types

Fonctions et données statiques

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.

Exercice

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.

class X {

public:static int i;static const int I = 1;

};

class Y {public:

static int i;static const int I = 3;

};

int X:: i;int Y:: i;int i; const int I = 2;

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 objetX(0) car le constructeurX(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 objetX(0) car le constructeurX(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 explicitementEn 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

É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 >>.

Exercice

Nouveaux types

Portée des identificateurs

Il y en a 5!

Portée globalePortée de fichierPortée de bloc

Portée de classePorté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 autresFichiers 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êmes’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

{…{

…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.

L’allocation de mémoire est faite statiquement à la compilation, initialisation à 0 (comme toutes les variables globales non initialisées en C).

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

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();}

};

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;

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();

Nouveaux types

Portée des identificateurs

Portée d’espace de nommagePourquoi ?

Lorsque l’on se linke avec uneBibliothèque on réutilise les idfsQui y sont définis. On ne peut pasDéfinir d’idf ayant le même nomQue 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>

}

Nouveaux types

Portée des identificateurs

Portée d’espace de nommageComment ?

Chaque fournisseur de bibliothèqueDoit encapsuler ses déclarations dansUn espace de nommage.

Les STL, les librairies standards du C++,Sont dans l’espace de nommage « std ».

namespace <idf> {<declarations>

}

Nouveaux types

Portée des identificateurs

Portée d’espace de nommage

Espace de nommage « anonyme » est équivalent à l’usage de « static » mais sansavoir 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.

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>

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

1. Ce n’est pas une blague de mauvais goût!2. Ce sont des membres dits « mutables »3. Mutable n’est pas compatible avec const et static4. Mutable signifie « ne sera jamais constant »

Permet au programmeur de changer la valeur d’une donnée membre mêmeSi 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;cout << x.lireI() << endl;

Programmation orienté objet

Avantages de la programmation par objetHéritage

Persistance

Concept objet avancé en C++: Objets homogènesClasses abstraites Structures hétérogènes

Héritage multiple Gestion des exception

Programmation orienté objet

1 classe = 1 module

Parties privée et publique des classes

Interface de la classe = type abstrait + généricité (templates)

Héritage

Modularité

Encapsulation

Abstraction

Extensibilité

Programmation orienté objet

class X: <mode> Y {

…}

publicprotectedprivate

Mode

Syntaxe Mode Ancètre DérivéePrivate Private Inaccessible

Protected Private Public Private

Protected Private Inaccessible

Protected Protected Public Protected

Public Private Inaccessible Protected Protected Public Public

Programmation orienté objet

class X{public: int x1;protected: int x2;private: int x3;};

X x;

cout << x.x1 << endl;Cout << x.x2 << endl; //NONCout << x.x3 << endl; //NON

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; //NONCout << y.x3 << endl; //NON

Héritage « public »

Programmation orienté objet

class Y : protected X {public:

Y() { x1 = 0; //protected x2 = 0; //protected

x3 = 0; //NON }

};

Y y;

Cout << y.x1 << endl; //NONCout << y.x2 << endl; //NONCout << y.x3 << endl; //NON

Héritage « protected »

class X{public: int x1;protected: int x2;private: int x3;};

X x;

cout << x.x1 << endl;Cout << x.x2 << endl; //NONCout << x.x3 << endl; //NON

Programmation orienté objet

class Y : private X {public:

Y() { x1 = 0; //private x2 = 0; //private

x3 = 0; //NON }

};

Y y;

Cout << y.x1 << endl; //NONCout << y.x2 << endl; //NONCout << y.x3 << endl; //NON

Héritage « private »

class X{public: int x1;protected: int x2;private: int x3;};

X x;

cout << x.x1 << endl;Cout << x.x2 << endl; //NONCout << x.x3 << endl; //NON

Programmation orienté objet

Construction des objets

Nouvellesdonnéesmembres

Redéfinitiond’anciennes

donnéesmembres

Donnéesmembres

classeancêtre

Un objet d’une classedérivée contient

Programmation orienté objet

Contenu de la classe dérivée

Nouvelles+redéfinition

fonctionsmembres

Nouveaudestructeur

Fonctionsmembres

classeancêtre

Une classedérivée contient

Nouveauxconstructeurs

Pas les « friend »

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; //nouveauvoid a() {…} // redéfinivoid b() {…} // nouveauint a() {…} // nouveau

};

Programmation orienté objet

Construction des objets

class X{public:

void a() {…} };

class Y : public X {public:

void a() {…} // redéfinivoid b() {

X::a();// autre classea(); //local

}};

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 « :: »

Programmation orienté objet

Construction des objets

class X{public:

X() {cout << ‘x’;} };

class Y : public X {public:

Y() { cout << ‘y’;}};

Lors de la construction d’un objet des constructeurs de toute la hiérarchieDes classes sont appelées dans l’ordre de la hiérarchie « top-down ».

class Z : public Y {public:

Z() { cout << ‘z’;}};

int main() { Z z; cout << endl;}

Programmation orienté objet

Construction des objets

class X{public:

int i;X(int n) { i = n;}

};

class Y : public X {public:

int j; Y(int n): X(n) { j = n;}

};

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 Z : public Y {public:

int k;Z(int n): Y(n) { k = n;}

};

Programmation orienté objet

Destruction des objets

class X{public:

~X() { cout << ‘X’;} };

class Y : public X {public:

~Y() {cout << ‘Y’;}};

Lors de la destruction d’un objet, les destructeurs de toute la hiérarchiedes classes sont appelées dans L’ordre de la hiérarchie « bottom-up »

class Z : public Y {public:

~Z() {cout << ‘Z’;}

};

int main() { Z z; cout << endl;}

Programmation orienté objet

Liaison dynamique

class X{public:

virtual void a() { < cout << ‘X’ << endl;}

};

class Y : public X {public:

virtual void a() { cout << ‘Y’ << endl;}

};

C’est la possibilité de ne lier qu’au moment de l’exécution la méthodeà invoquer (pas possible pour les constructeurs).

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();//?

Programmation orienté objet

Liaison dynamique

class X{public:

virtual void a() { < cout << ‘X’ << endl;}

};

class Y : public X {public:

virtual void a() { cout << ‘Y’ << endl;}

};

C’est la possibilité de ne lier qu’au moment de l’exécution la méthodeà invoquer (pas possible pour les constructeurs).

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();//?

Programmation orienté objet

Liaison dynamique

class X{public:

void a() { < cout << ‘X’ << endl;}

};

class Y : public X {public:

void a() { cout << ‘Y’ << endl;}

};

Que se passe-t-il sans « virtual » ?

X *x = new X();Y *y = new Y();

X->a();Y->a();x = y;X->a();//?

Programmation orienté objet

Classe abstraite

C’est une classe « mère » dans laquelle au moins une méthode est virtuelleet 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 classeabstraire. Inutile de spécifier les constructeurs et le destructeur.

Programmation orienté objet

Classe abstraite

class X{public:

virtual void a() = 0;virtual void b() = 0;…

};

class Y : public X {public:

virtual void a() { cout << ‘a’ << endl;}virtual void b() { cout << ‘b’ << endl;}

};

Classe abstraire

Classe non abstrairevirtual est facultatif

Programmation orienté objet

Héritage multipleclass X{public:

int i; X(int n) { i = n;}void 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() {…}

};

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();

}};

Programmation orienté objet

Héritage multipleclass X{public:

int i; X(int n) { i = n;}void 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() {…}

};

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();

}};

Programmation orienté objet

Héritage multipleclass X{public:

int i; X(int n) { i = n;}void 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() {…}

};

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();

}};

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érieurPuisse 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:int i;

};

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:int i;

};

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;

};