View
104
Download
0
Category
Preview:
Citation preview
Structures de donnéesIFT-10541
Abder AlikacemAbder Alikacem
Introduction au langage C++
Département d’informatique et de génie logiciel
Édition Septembre 2009
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 = ⅈ 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;
Recommended