4 ime Classe Mardi 30 Septembre CSI 2572
4 ième Classe (Mardi, 30 Septembre) CSI 2572
2 C A C++ Les commentaires Il y a deux types de commentaires en C++ : les commentaires mono ligne et les blocs de commentaire. Les commentaires mono ligne commencent par les symboles // et permettent de placer en commentaire la fin d'une ligne comme le montre le fragment de code suivant : int i; // fin de la ligne en commentaire
2 C A C++ II Les types références En C, il n`y avait qu'un seul mode de passage des paramètres : le passage par valeur (même le passage par adresse correspond à passer une adresse par valeur ). Supposons qu'une fonction ait besoin de modifier la valeur de l'un de ses paramètres. Comme seul le passage par valeur est supporté, il ne reste plus qu'à passer un pointeur sur l'objet à modifier. En C++, on peut aussi passer par référence. Problèmes: Et si on passe un argument par valeur qui occupe beaucoup d’espace? La copie doit être mise sur le stack… Ce qui est inutile si on ne désire pas la modifier.
Les types références (syntaxe) Une référence à un paramètre de fonction en C++ est un alias pour son argument correspondant. Pour indiquer qu’un paramètre de fonction doit être passé par réference, il suffit de faire suivre le type du paramètre de la fonction par le symbole &: char* foo(int &a, char b); C’est la même chose pour la déclaration d’un alias à une variable (une variable de type référence): int a; int & ar = a; Les variables de type référence doivent être initialisées lors de leur déclarations. Attention: La syntaxe est néanmoins trompeuse car le signe de référence "&" est le même que celui de la prise d'adresse.
/* En C d'abord */ void *a, int *b) { // Etswap. Entiers(int maintenant en C++ int c = *a; void swap. Entiers(int &a, int &b) { *a *b; int=c=a; *b c; a ==b; } b = c; } int main(int, argc, char **) {*argv[]) { int i=5; int j=6; swap. Entiers(&i, &j); swap. Entiers(i, j); return 0; 0; } }
#include <iostream> int main(int, ATTENTION: char **) { int i=5; int j=10; int &r=i; cout << i << endl; // Affiche 5 cout << r << endl; // Affiche 5 ATTENTION: Les références qui ont été crées en r++; variables ne peuvent plus être cout << r << endl; // Affiche 6 réassignées. cout << i << endl; // Affiche 6 r=j; r++; cout << r << endl; // Affiche 11 cout << j << endl; // Affiche 10 cout << i << endl; // Affiche 11 ! return 0; }
2 C A C++ III Surcharge des fonctions La surcharge est un mécanisme qui permet de donner différentes signatures d'arguments à une même fonction. Vous pouvez nommer de la même façon des fonctions de prototypes différents. L'intérêt est de pouvoir nommer de la même manière des fonctions réalisant la même opération à partir de paramètres différents.
Exemple: Vous travailliez sur un système de gestion d'interface utilisateur à base de fenêtres. Chaque fenêtre est peut être identifiée de 2 manières différentes: 1. Un identificateur numérique 2. le nom : une chaîne de caractères Vous voulez écrire 2 fonctions qui permettent de récupérer un pointeur sur une fenêtre en utilisant soit l'identificateur numérique, soit le nom. Avec un système sans surcharge, il vous faudrait utiliser 2 noms de fonctions différents, par exemple : Window *Get. Window. Ident(unsigned long identificateur. Numerique); Window *Get. Window. Name(const char *nom);
Surcharger vous permet d'utilisez le même nom : Window *Get. Window(unsigned long identificateur. Numerique); Window *Get. Window(const char *nom);
Le compilateur s'y retrouve parce que le nom « interne » de la fonction contient la liste des paramètres. À la compilation, en consultant la liste des paramètres effectifs, le compilateur établit quelle est la forme de la fonction à appeler. Il est impossible d'avoir des fonctions qui ne diffèrent que par leur type de retour. En effet, le compilateur ne peut pas les distinguer si on omet de récupérer la valeur retournée.
2 C A C++ IV Les paramètres avec valeurs par défaut C++ vous permet de spécifier une valeur par défaut au arguments de vos fonctions. De 7 manière, quand vous utilisez la fonction, vous n'avez à spécifier ces arguments que lorsque leur valeur diffère du défaut. Les valeurs par défaut ne peuvent être spécifiés que lors de la déclaration de la fonction et ne doivent pas être rappelés lors de définition de la fonction. Exemple: int Show (Window *p, unsigned int mode. Ouverture = 1);
Le nombre des paramètres avec des valeurs par défaut n'est pas limité. En fait, tous les paramètres peuvent prendre une valeur par défaut. Toutefois, seuls les derniers paramètres de la fonction peuvent avoir une valeur par défaut. Par exemple, le code suivant est illégal : void f 1(int i, double angle=0. 0, double longueur); De même, avec : void f 2(double angle=0. 0, double longueur=50. 0); L'appel suivant est illégal : f 2(, 35. 0); Vous ne pouvez omettre que les valeurs derniers paramètres.
2 C A C++ V Les nouvelles primitives d'entrées / sorties Les fonctionnalités d'entrées / sorties en C++ sont orientées objet. Elles reposent sur la notion de flux (en anglais: streams). Leur utilisation se fait principalement au travers des opérateurs de redirection en sortie "<<" et en entrée ">>". Ainsi, l'écriture à l'écran d'une expression se traduit par le code suivant : cout << expression; Cela sous entend la surcharge de l'opérateur << pour le type de donnée correspondant à expression. Ce travail a été réalisé pour la plupart des types atomiques du C++. Il vous appartiendra de le faire pour vos propres classes.
Les flux standards Il y a 3 objets de type flux: Objet Flux Périphérique "C" Description cout stdout Flux associé à la sortie standard (écran) cin stdin Flux associé à l'entrée standard (clavier) cerr stderr Flux associé à la sortie des erreurs (écran)
Les opérateurs << sont prévus pour être chaînés. Vous pouvez donc les cumuler sur la même ligne de code, par exemple: int i; int j; cout << i << " " << j << endl; La constante symbolique endl vous permet de changer de lignes indépendamment de l'architecture dans laquelle le programe évolue.
Comme avec les printf, Il. Les est appels possible sont de spécifier desde formats réalisés la d'affichage à la manière droite des fameux vers"%6. 2 f". la gauche. Pour cela, il faut utiliser des flux. L'exemple Les manipulateurs expressions de que vous suivant créé l'équivalent d'un printf("6. 2 f", r); 36. 14 #include <iostream> 6 5 #include <iomanip> using namespace std; 6 affichez sont présentées dans l'ordre que vous avez demandé mais elles ont été évaluées dans l'ordre inverse. int main(int, char **) { double r=36. 145278; int i=5; cout << setw(6) << setprecision(2) << r << endl; cout << i << " " << i++ << endl; cout << i << endl; return 0; }
2 C A C++ VI Allocation de mémoire en C++ intplus * pdes = "anciens" (int*)malloc(sizeof(int)); En malloc et free du C, C++ possède un free(p); jeu d'opérateurs d'allocation/désallocation de mémoire : nouveau p = et 0; delete. new int p =créés (int*)calloc(10, sizeof(int)); Ils ont* été principalement pour la gestion dynamique des free(p); objets, mais on peut les utiliser également pour des variables p = 0; simples. int * p = new int; delete p; p = 0; int * p = new int[10]; delete[] p; p = 0;
double ** alloc_matrix(int n, int m) { double ** M = new double*[n]; for(int i=0; i< n; ++i) M[i] = new double[m]; return M; } void free_matrix(double ** M, int n) { for(int i=0; i< n; ++i) delete[] M[i]; delete[] M; }
2 C A C++ VII La programmation orientée objet cherche à modéliser informatiquement des éléments du monde réel en entités informatiques appelées objets. Les objets sont des données informatiques regroupant les principales caractéristiques des éléments du monde réel (taille, couleur, . . . ). La difficulté du processus de modélisation est dans la création d'une représentation abstraite d'entités ayant une existence matérielle (chien, voiture, ampoule, . . . ) ou bien virtuelle (sécurité sociale, temps, . . . ).
Dans le monde réél, deux T-shirts peuvent être identique et distincts. La classe , c'est la structure d'un objet, c'est-à-dire la déclaration de l'ensemble des entités qui composeront un objet. Une classe peut être considérée comme un moule à partir duquel on peut créer des objets. Un objet est donc "issu" d'une classe. C'est une instanciation d'une classe, c'est la raison pour laquelle on pourra parler indifféremment d'objet ou d'instance.
Une classe est composée de deux parties: • Les attributs (parfois appelés données membres): il s'agit des données représentant l'état de l'objet • Les méthodes (parfois appelées fonctions membres): il s'agit des opérations applicables aux objets Si on définit la classe voiture, les objets Toyota_Civic, Mustang 2003 seront des instanciations de cette classe. Il pourra éventuellement exister plusieurs objets Toyota_Civic, différenciés par leur numéro de série. Deux instanciations de classes pourront même avoir tous leurs attributs égaux sans pour autant être le même objet.
Dans ce modèle, un véhicule est représenté par une chaîne de caractères (sa marque) et trois entiers : la puissance fiscale, la vitesse maximale et la vitesse courante. chaque objet véhicule aura sa propre copie de ses données : on parle alors d'attribut d'instance. L'opération d'instanciation qui permet de créer un objet à partir d'une classe consiste précisément à fournir des valeurs particulières pour chacun des attributs d'instance.
> La déclaration de la classe commence par le mot clef class et est encadrée par une paire d'accolades. L'accolade finale est suivie d'un point virgule. Les membres déclarés après le mot clef public forment l'interface de la classe. Ceux qui suivent le mot private sont invisibles de l'utilisateur ; > L'ordre de déclaration des méthodes et des attributs est laissé au libre arbitre du programmeur. > La déclaration des attributs est semblable à la déclaration d'une variable alors que celle d'une méthode ressemble à s'y méprendre au prototype d'une fonction ! Néanmoins, il ne faut pas oublier que chaque méthode possède un argument caché : l'objet sur lequel elle est invoquée.
H Lors de l'implémentation des méthodes, il est nécessaire de préfixer le nom de la méthode implémentée du nom de la classe suivi de ': : '. Par exemple, l'implémentation de la méthode deplacer. Vers de la classe Point se fait en spécifiant: Point: : deplacer. Vers H Le constructeur est une méthode particulière qui porte le même nom que la classe et dont le but est d'initialiser les attributs lors de la création d'un objet. H Les méthodes x et y sont déclarées constantes à l'aide du mot clef const. Cela signifie que leur code n'affecte en aucune manière la valeur des attributs de l'objet cible.
De la même manière, qu'une classe, un objet est caractérisé par: • Attributs: Il s'agit des données caractérisant l'objet. Ce sont des variables stockant des informations d'état de l'objet • Méthodes (appelées parfois fonctions membres): Les méthodes d'un objet caractérisent son comportement, c'est-àdire l'ensemble des actions (appelées opérations) que l'objet est à même de réaliser. Ces opérations permettent de faire réagir l'objet aux sollicitations extérieures (ou d'agir sur les autres objets). De plus, les opérations sont étroitement liées aux attributs, car leurs actions peuvent dépendre des valeurs des attributs, ou bien les modifier • De plus, un objet a aussi une Identitée
Encapsulation, polymorphism et héritage • Encapsulation: • Le rassemblement des données et du code les utilisant dans une entité unique (objet). • La séparation nette entre la partie publique d'un objet (ou interface) seule connue de l'utilisateur de la partie privée ou implémentation qui reste masquée. • Polymorphism: • Une méthode peut adopter plusieurs formes différentes. • Héritage • Possibilité de définir des familles de classes traduisant le principe de généralisation / spécialisation. « La classe dérivée est une version spécialisée de sa classe de base »
Encapsulation I L'encapsulation consiste à masquer l'accès à certains attributs et méthodes d'une classe. Pourquoi masquer? C Cacher les détails d'implémentation des objets à l'utilisateur permet de modifier, par exemple la structure de données interne d'une classe (remplacer un tableau par une liste chaînée) sans pour autant entraîner de modifications dans le code de l’utilisateur, l’interface n’étant pas atteinte. C Abstraction de données : la structure d'un objet n'est pas visible de l'extérieur, son interface est constituée de messages invocables par un utilisateur. La réception d'un message déclenche l'exécution de la méthode correspondant à ce message. C Abstraction procédurale : Du point de vue de l'extérieur (c’est-à-dire en fait du client de l’objet), l'invocation d'un message est une opération atomique. L'utilisateur n'a aucun élément d'information sur la mécanique interne mise en œuvre. Par exemple, il ne sait pas si le traitement requis a demandé l’intervention de plusieurs méthodes ou même la création d’objets temporaires etc.
Encapsulation II En C++, on choisit le paramètres d'encapsulation à l'aide des mots clés : G private : les membres privés ne sont accessibles que par les G protected G public : les membres publics sont accessibles par tous. La fonctions membres de la classe. : les membres protégés sont comme les membres privés. Mais ils sont aussi accessibles par les fonctions membres des classes dérivées. partie publique est appelée interface. Les mots réservés private , protected et public peuvent figurer plusieurs fois dans la déclaration de la classe. Le droit d'accès ne change pas tant qu'un nouveau droit n'est pas spécifié.
class Avion { public : // fonctions membres publiques void init(char [], char *, float); void affiche(); private : // membres privées char immatriculation[6], *type; float poids; // fonction membre privée void erreur(char *message); }; // n'oubliez pas ce ; après l'accolade
• Les fonctions membres sont définies dans un module séparé ou plus loin dans le code source. • Syntaxe de la définition hors de la classe d'une méthode : type Classe: : nom_méthode( paramètres_formels ) { // corps de la fonction }
class Avion { void Avion: : init(char m[], char *t, float p) { if ( strlen(m) != 5 ) { erreur("Immatriculation invalide"); public : // fonctions membres publiques strcpy( immatriculation, "? ? ? "); } void init(char [], char *, float); else void affiche(); strcpy(immatriculation, m); type = new char [strlen(t)+1]; strcpy(type, t); private : // membres privées poids = p; } char immatriculation[6], *type; float poids; // fonction membre privée void Avion: : affiche() { void erreur(char *message); cout << immatriculation << " " << type; cout << " " << poids << endl; } }; // n'oubliez pas ce ; après l'accolade
Comme pour struct , le nom de la classe représente un nouveau type de donnée. On peut donc définir des variables de ce nouveau type; (créer des objets ou des instances) Avion av 1; // une instance simple (statique) Avion *av 2; // un pointeur (non initialisé) Avion compagnie[10]; // un tableau d'instances av 2 = new Avion; // création (dynamique) d'une instance
Après avoir créé une instance (de façon statique ou dynamique) on peut accéder aux attributs et méthodes de la classe. Cet accès se fait comme pour les structures à l'aide de l'opérateur. ou ->. av 1. init("FGBCD", "TB 20", 1. 47); av 2 ->init("FGDEF", "ATR 42", 80. 0); compagnie[0]. init("FEFGH", "A 320", 150. 0); av 1. affiche(); av 2 ->affiche(); compagnie[0]. affiche(); av 1. poids = 0; // erreur, poids est un membre privé
class Nombre { public : void setnbre(int n) { nbre = n; Certaines méthodes d'une classe ne doivent (ou ne peuvent) } pas modifier les valeurs des données membres de la classe, ni retourner une référence non constante ou un pointeur non // méthodes constant d'une membre : on{ dit que ce sont des intdonnée getnbre() const fonctions membres return constantes. nbre; } Ce type de déclaration renforce les contrôles effectués par le void affiche() const; compilateur et permet donc une programmation plus sûre sans private coût d'exécution. Il est donc très souhaitable d'en : déclarer aussi intsouvent nbre; que possible dans les classes. }; inline void Nombre: : affiche() const { cout << "Nombre = " << nbre << endl; }
class vector { class line_segment private: { double x, y; /* coordinate */ private: public: vector(); a, b; /* end points */ /* default constructor: <0, 0> */ }; public: vector(double, double); line_segment(vector, /* constructor: <x, y> vector); */ /* constructor: a, b */ double get_x() const; vector doubleget_a() get_y()const; vector get_b() const; void set_a(vector); void set_b(vector); vector middle() const;
- Slides: 36