Informatique III: Programmation en C++ Lundi 24 Octobre 2005 Chargé de cours François Fleuret francois.fleuret@epfl.ch Assistant Mathieu Salzmann mathieu.salzmann@epfl.ch Assistants étudiants Ali Al-Shabibi ali@olympe.ch Sandra Pralong sandra.pralong@epfl.ch Guillaume Raymondon alkarn@gmail.com Page web http://cvlab.epfl.ch/~fleuret/i3sv2 1 2 Examens Un partiel de deux heures après 6 semaines de cours, et un examen final. La note finale sera ( ) Npartiel + N examen N finale = max, N examen 2 Introduction 3 4
Aujourd hui Description en français de ce qu est la programmation orientée objets. Introduction à la syntaxe non-objet du C++ (très semblable au C) Quelques exercices Origine du C++ Extension objet du C Développé initialement par Bjarn Stroustrup en 1983 1985 Normalisé ISO en 1998 puis 2002 Le C date de 1969 1973 5 6 Autre langages Orientés Objets Smalltalk (1980?) Objective-C (Apple, Brad Cox, 1986) Python (Open source, Guido van Rossum, 1990) Java (Sun Microsystem, 1995) C# (Microsoft, 2001) La plupart des langages admettent une extension objets Caml PERL Basic PHP 7 8
Avantages du C++ Objectif de ce cours Comprendre la philosophie objets Apprendre la syntaxe propre au C++ Similarités syntaxiques avec le C Facilement interfaçable avec le C Proche de la machine (rapide) Possède de nombreux mécanismes modernes de l orienté-objets Très répandu Excellents compilateurs open-source (GNU) 9 10 Défauts du C++ Similarités syntaxiques avec le C Pas de gestion automatique de la mémoire Pas de protection de la mémoire Gestion des exceptions facultative Pousse-au-crime La POO (programmation orientée objets) Manière de structurer les programmes Ne nécessite pas un langage particulier (cf. Gtk) Interactions entre des objets Un objet peut être vu comme une struct accompagnée de fonctions (méthodes) pour manipuler les données qu elle contient. 11 12
Abstraction: exemple Abstraction des données Les informations sont manipulées à l aide de méthodes qui cachent les représentations Polymorphisme: des données différentes peuvent être manipulées de manières identiques. Intégrité des données On peut prendre un type d objets déjà existant et lui rajouter des données et/ou des méthodes pour en faire un nouveau type (héritage). On veut manipuler des ensembles d entiers. On peut imaginer trois situations différentes, correspondant à trois représentations de ce concept: 1. C est un singleton int k; 2. C est un sous-ensemble de 0...N bool set[n+1]; 3. C est un ensemble quelconque struct { int nb; int *valeurs; } Il serait très difficile en C de programmer une fonction unique capable de manipuler ces trois représentations. 13 14 En POO, on définit une liste de méthodes suffisante pour manipuler une variable du type abstrait. Par exemple ici 1 // Combien d entiers dans notre ensemble 2 int size(); 3 // Retourne le n-ieme (premier pour n = 0) 4 int get(int n); 5 // Dit si un entier est ou pas dans l ensemble 6 bool contains(int n); Ces méthode s appliqueraient à un objet (i.e. une variable du type ensemble d entiers ), et seraient programmées pour chaque type sous-jacent. Héritage: exemple Imaginons que l on ait définit un type Rectangle et que l on ait écrit un grand nombre de fonctions pour les manipuler. Si on veut à présent manipuler des étiquettes qui sont des rectangles avec un texte, il suffit d étendre le type Rectangle pour rajouter un nouveau champ de donnée et les méthodes pour manipuler ce nouveau champ. On pourra utiliser des Etiquette avec toutes les fonctions écrites pour les Rectangle 15 16
Avantage de la POO Intégrité des données On peut interdire d accéder directement aux données dans un objet, et imposer que les modifications se fassent à travers des méthodes qui vérifient des contraintes d intégrité. Par exemple, on pourrait imposer que la largeur et la hauteur d un rectangle soient positives. Simplicité de conception Modularité Réutilisabilité (héritage) Polymorphisme (le même code manipule des structures de données différentes) Contrôle de l intégrité des données Gigantesques librairies déjà écrites (java, C#) 17 18 Défauts de la POO Lourdeur pour des choses simples Coût algorithmique pour manipuler des types simples Syntaxe non-objet du C++ Les types fondamentaux ne sont pas des objets en C++. 19 20
Programme minimum Le C++ est un sur-ensemble de C Tout ce qui était syntaxiquement valide en C est valide en C++ Quelques changements plus élégants 1 #include <iostream> 2 3 using namespace std; 4 5 int main(int argc, char **argv) { 6 cout << "Hello world!" << endl; 7 return 0; 8 } Nous reviendrons plus tard sur iostream, using namespace et cout, etc. 21 22 Types fondamentaux int pour les entiers float et double pour les réels char pour les caractères bool pour les booléens Les définitions de fonctions et de struct sont identiques. Exécution conditionnelle 1 if( expression booleenne ) { 2 partie à exécuter si le test est vrai 3 } 1 if( expression booleenne ) { 2 partie à exécuter si le test est vrai 3 } else { 4 partie à exécuter si le test est faux 5 } 23 24
Boucles 1 for(initialisation; test; increment) { 2 partie à exécuter tant que le test est vrai 3 } 1 while(test) { 2 partie à exécuter tant que le test est vrai 3 } 1 do { 2 partie à exécuter tant que le test est vrai, 3 toujours exécutée une fois 4 } while(test) Opérateurs Opérateur Fonction Opérateur Fonction + addition && Et logique - soustraction Ou logique * multiplication & Et bit à bit / division Ou bit à bit % reste euclidien! Non logique 25 26 Différences non-objet entre C++ et C Compilateur, noms de fichiers En C++, les fichiers source se terminent par l extension.cpp (au lieu de.c en C). Sous UNIX, le compilateur est g++ (au lieu de gcc pour le compilateur C). On peut utiliser emacs et la fenêtre terminal comme pour les programmes en C. 27 28
Librairies standard Nous utiliserons trois librairies standard du C++ Les entrées-sorties iostream Les manipulations de fichiers fstream Les opérations mathématiques cmath Les définitions des fonctions d une librairie doivent être inclues dans le programme à l aide de #include, et il faut rajouter using namespace std; après les includes pour pouvoir utiliser directement la plupart des fonctions. Affichages et saisies À la place des vénérables printf et scanf, le C++ offre le concept de stream. Ce sont des objets abstraits auxquels on peut transmettre (ou dans lesquels on peut lire) des types de données quelconques. 29 30 Utilisation de cout Pour afficher les entiers de 1 à 100 on fera Les ostream ( output stream ) permettent d écrire des valeurs (à l écran ou dans un fichier). On envoie une valeur dans un ostream avec l opérateur <<. Les istream ( input stream ) permettent de lire des données (depuis le clavier ou un fichier). On lit une valeur dans un istream à l aide de l opérateur >>. 1 #include <iostream> 2 3 using namespace std; 4 5 int main(int argc, char **argv) { 6 for(int i = 0; i < 100; i++) cout << i << endl; 7 return 0; 8 } Le symbole cout représente le ostream particulier associé à la sortie standard. L opérateur << prend un ostream à gauche, un type quelconque à droite, et retourne un ostream (en plus d avoir fait l affichage proprement dit). 31 32
Utilisation de cin Détails de l évaluation Une expression telle que 1 cout << "Mon entier vaut " << n << endl; Est interprétée comme 1 ( ( cout << "Mon entier vaut " ) << n ) << endl; par le compilateur. Si l on veut demander un entier à l utilisateur et afficher tous les entiers dont le carré est plus petit. 1 int bound; 2 cin >> bound; 3 for(int k = 0; k * k <= bound; k++) 4 cout << k << endl; Le symbole cin représente le istream particulier associé à l entrée standard. L opérateur >> lit une valeur du type donné dans le istream à gauche et l affecte à la variable à droite. 33 34 Précision, nombre de chiffres Un grand nombre de paramètres de formatages peuvent être fixés. En particulier le nombre de chiffres significatifs. 1 double x = 11.0/17.0; 2 cout << "x = " << x << endl; 3 cout.precision(3); 4 cout << "x = " << x << endl; 5 cout.precision(25); 6 cout << "x = " << x << endl; affiche x = 0.647059 x = 0.647 x = 0.6470588235294117973595007 35 36
Définition de variables Comme en C, une variable est définie par son type, et par le nombre d éléments dans le cas d un tableau. La partie de programme où elle existe s appelle son scope. Il commence à l endroit de la définition et se termine à la fin du plus petit bloc d instructions qui contient la définition. On peut définir des variables n importe où en C++, y compris dans l initialisation d une boucle for. Le scope est alors la boucle elle-même. Exemple 1 int fonction_bizarre(int k) { 2 int a = 0; 3 for(int c = 0; c < k*k; c++) { 4 int d = c + c * c + c * c * c; 5 if(c%k == 0) a += d; 6 } 7 return a; 8 } Le scope de k va de la ligne 1 à la } ligne 8, celui de a va de la ligne 2 à la } ligne 8, le scope de c va de 3 à la } ligne 6, et celui de d de 4 à la } ligne 6. 37 38 Masquage de variables 1 #include <iostream> 2 3 using namespace std; 4 5 int main(int argc, char **argv) { 6 int a = 3; 7 cout << "1: a = " << a << endl; 8 { 9 int a = 5; 10 cout << "2: a = " << a << endl; 11 } 12 cout << "3: a = " << a << endl; 13 return 0; 14 } On a défini une deuxième variable a qui masque la première dans le bloc d instruction où elle est définie. Le programme affiche 1: a = 3 2: a = 5 3: a = 3 39 40
Exercice: Calcul de la surface d un disque (3min) Écrire un programme qui demande à l utilisateur le rayon d un disque, puis affiche la surface avec 3 chiffres significatifs. La constante π est définie dans cmath par le symbole M PI. 1 #include <iostream> 2 #include <cmath> 3 4 using namespace std; 5 6 int main(int argc, char **argv) { 7 float radius; 8 cout << "Entrez le rayon: "; 9 cin >> radius; 10 cout.precision(3); 11 cout << "La surface du disque est " 12 << M_PI * radius * radius << endl; 13 return 0; 14 } 41 42 Exercice: Afficher les diviseurs d un entier (5min) Écrire un programme qui demande à l utilisateur un entier puis affiche la liste de ses diviseurs. Le programme re-demandera l entier jusqu à en obtenir un strictement positif. 1 #include <iostream> 2 3 using namespace std; 4 5 int main(int argc, char **argv) { 6 int n; 7 do { cin >> n; } while(n <= 0); 8 for(int k = 1; k <= n; k++) 9 if(n%k == 0) cout << k << endl; 10 return 0; 11 } 43 44
Allocation mémoire (exemple) Allocation et déallocation de mémoire L allocation dynamique de mémoire se fait en C à l aide des deux appels systèmes malloc et free. En C++, trois nouveaux opérateurs remplissent la même fonction: new, delete et delete[]. Le premier permet d allouer une variable ou un tableau, et les deux autres de libérer des zones allouées. Le bout de source suivant alloue dynamiquement d abord un double unique, puis un tableau de 150 double. double *a; a = new double; *a = 3.14; delete a; a = new double[150]; for(int i = 0; i < 150; i++) a[i] = i; delete[] a; 45 46