Maîtrise d’informatique

Slides:



Advertisements
Présentations similaires
La programmation orientée objet avec Java L3-MIAGE Plan
Advertisements

Erratum C Surcharge For(int x=0; … 2.
Formation universitaire à .NET: Introduction à C#
C++ 6ème cours Patrick Reuter maître de conférences
Spécialisation/généralisation Héritage Polymorphisme.
GEF 243B Programmation informatique appliquée
Langages objet Définitions Traduction des méthodes en C++
C.
Paramètres et pointeurs
Leçon 3 : Héritage IUP 2 Génie Informatique
Chapitre III Héritage (début)
Principes de programmation (suite)
2ième Classe (Mercredi, 13 Octobre) C++ Intro CSI2572.
Points importants de la semaine Les fonctions. La portée. La passage par copie. Les tableaux.
Structures de données linéaires
Programmation orientée objet
CSI3525: Concepts des Langages de Programmation Notes # 12: Implementation des Sous-Programmes ( Lire Chapitre 9 )
Langage Oriente Objet Cours 4.
Leçon 6 : Structures de données dynamiques IUP 2 Génie Informatique Méthode et Outils pour la Programmation Françoise Greffier.
8PRO100 Éléments de programmation Allocation dynamique de la mémoire.
Système d’exploitation : Assembleur
Page de garde C++ Le RTTI et les opérateurs de cast Maîtrise dinformatique Février 2002.
IFT-2000: Structures de Données Listes chaînées Dominic Genest, 2009.
Introduction au paradigme objet Concepts importants surcharge (overload) redéfinition (override) Définition d’une classe Définition des attributs.
77 Utilisation des classes (suite). 7-2 Objectifs A la fin de ce cours, vous serez capables de : Définir des méthodes surchargées dans une classe Fournir.
Quest-ce quune classe dallocation? Une classe dallocation détermine la portée et la durée de vie dun objet ou dune fonction.
Langage Oriente Objet Cours 2.
Leçon 2 : Surcharge des opérateurs IUP 2 Génie Informatique Méthode et Outils pour la Programmation Françoise Greffier Université de Franche-Comté.
Les pointeurs Modes d’adressage de variables. Définition d’un pointeur. Opérateurs de base. Opérations élémentaires. Pointeurs et tableaux. Pointeurs et.
Page de garde C++ Les exceptions Maîtrise dinformatique Février 2002.
Structures de données IFT-2000
Introduction au paradigme orienté-objet (suite)
Présentation Structures de Données et TDA
1 IFT 6800 Atelier en Technologies dinformation Le langage de programmation Java chapitre 3 : Classes et Objects.
COURS DE PROGRAMMATION ORIENTEE OBJET :
Méthode et Outils pour la Programmation
Leçon 1 : notion dobjet IUP Génie Informatique Besançon Méthode et Outils pour la Programmation Françoise Greffier Université de Franche-Comté.
Structures de données IFT-2000
Structures de données IFT-2000 Abder Alikacem Retour sur les listes ordonnées Département dinformatique et de génie logiciel Édition Septembre 2009.
Plan cours La notion de pointeur et d’adresse mémoire.
Héritage Licence Informatique Besançon Méthode et Outils pour la Programmation Françoise Greffier.
Héritage et composition
2.1 - Historique Chapitre 2 : Introduction au langage C++
La notion de type revisitée en POO
11/04/ L'héritage Cours 7 Cours 7.
Variables et accès en Java. Déclaration des variables final transient static private Printer hp; transient => ne doivent pas être sérialisées volatile.
12/04/ Le polymorphisme Cours 8 Cours 8.
Cours C++ Fonctions Surcharge d’opérateurs Passage d’arguments
Tutorat en bio-informatique
Programmation Système et Réseau
ETNA – 1ème année Guillaume Belmas –
Les classes et les objets Les données finales class A { … private final int n = 20 ; // la valeur de n est définie dans sa déclaration … } class A { public.
CSI 3525, Implémentation des sous-programmes, page 1 Implémentation des sous-programmes L’environnement dans les langages structurés en bloc La structure.
ISBN Chapitre 10 L'implémentation des sous- programmes.
Classe 1 CSI2572 Autres modificateurs de déclaration de variables: & volatile & register & static & auto & extern & const volatile Indique au compilateur.
Les classes Introduction aux Langages Orientés Objets
Cours 4 (14 octobre) Héritage. Chapitre III Héritage.
Héritage Conception par Objet et programmation Java
Conception de Programmes - IUT de Paris - 1ère année Quelques éléments du langage C++ Les références La surcharge de fonctions Les fonctions «
Patricia Renault UPMC 2005/2006
Les fonctions. Déclaration/Prototypes C’est une instruction fournissant au compilateur des infos sur une fonction qu’on envisage d’appeler par le suite.
PRO-1027 Programmation Scientifique en C
Langage de Programmation Orientée Objet : C++
CHAPITRE 10 Les sous-programmes 1. Sous-programme Suite d’instructions appelée de manière répétitive Par un programme Par plusieurs programmes distincts.
3ième Classe (Mardi, 23 Septembre) CSI2572. O jourd'8: E Allocation de mémoire E Déallocation de mémoire E Tableaux (n dimensions) E Arithmetique des.
Organisation de la mémoire pour le langage minimal Pr ZEGOUR DJAMEL EDDINE Ecole Supérieure d’Informatique (ESI)
Transcription de la présentation:

Maîtrise d’informatique Page de garde Inside C++ Yannis.BRES@cma.inria.fr Maîtrise d’informatique Février 2002

Qu’est-ce qu’un ordinateur ? Fondamentalement, un ordinateur c’est :  Un (ou des) processeur(s)  De la mémoire vive (non persistante)  Des périphériques de stockage persistants (disque dur, graveurs, bandes, …)  Des périphériques d’entrées-sorties (clavier, souris, carte vidéo, carte son, …)  … La mémoire vive a trois utilisations majeures :  Stockage des instructions (le code) des processus  Pile d’exécution des processus  Espace de tas des processus (là où (m|c)alloc et new allouent les blocs demandés) Le processeur contient des registres, dans lesquels sont mémorisés, pour le processus courant :  Le pointeur sur l’instruction en cours  Le pointeur courant de la pile  Les adresses de base des segments de code, de pile, de tas, …  Les premiers arguments des fonctions appelées, la valeur de retour, …  … Les registres servent aussi de "variables intermédiaires"  dans les calculs.

Qu’est-ce qu’un ordinateur ? La mémoire de pile sert à :  Préserver (empiler) les valeurs des registres pour pouvoir les restaurer (dépiler) ultérieurement, par exemple avant et après un appel de fonction  Passer les arguments des fonctions qui ne sont pas passés par des registres  Allouer de l’espace pour les variables locales et les objets locaux (+ alloca)  … Par la suite, nous considèrerons que nous travaillons sur une architecture 32 bits Intel, donc avec un support de pile d’exécution fourni par le processeur (ce qui n’est pas le cas de tous les processeurs). Petit rappel : C/C++ garantissent des relations d’ordre entre les tailles des types primitifs…

Qu’est-ce qu’un ordinateur ? code segment processeur ... mov reg1, [a] add reg1, [b] cmp reg1, [c] jge +5Fh mov [d], 69h jmp main+43h pc sp stack segment reg1 reg2 reg3 reg4 … … PC : Program Counter (pointeur d’instruction) SP : Stack Pointer (pointeur de pile)

Appel de fonction simple XXX f( … ) { … } stack L’adresse de f est connue à la compilation. sp Schéma d’un appel à f : registres caller-save Préservation des registres caller-save Empilement des arguments arguments Saut à la fonction après empilement de l’adresse de retour Réservation d’espace de pile pour les variables/objets locaux return address Préservation des registres callee-save variables locales f fait son boulot… Restauration des registres callee-save utilisés par la fonction registres callee-save Dépilement des variables locales Retour à l’instruction suivant l’appel de f Dépilement des arguments Restauration des registres caller-save

Appel de fonction simple Les registres caller-save sont les registres utilisés par la fonction appelante et que les fonctions appelées ont le droit de modifier. Les registres caller-save sont donc préservés par la fonction appelante même si la fonction appelée ne les modifie pas… Les registres callee-save sont les registres utilisés par la fonction appelée, qui sont donc préservés par la fonction appelée même si les fonctions appelantes ne les utilisent pas… Le stockage des variables locales dans la pile est le mécanisme permettant, par exemple, aux différentes "instances" d’une fonction récursive d’avoir chacune leurs propres variables locales. Les variables locales static ne sont que des variables globales à visibilité réduite. Les conventions d’appels (passage des arguments par registre ou dans la pile, ordre de passage des arguments, responsabilité des dépilements d’arguments, registres callee-save ou caller-save, …) dépendent à la fois de l’architecture matérielle (cf. doc constructeur) et du language (extern "C", extern "Pascal", …).

Appel de méthode non virtuelle Le mécanisme d’appel de méthodes static est strictement identique à celui des appels de fonctions. Les méthodes non-static ont un paramètre semi-caché supplémentaire : le pointeur constant this.

Fonctions et méthodes inline Le mécanisme d’inlining vise à réduire le coût des appels de fonctions/méthodes. Si, au moment de la génération de code pour un appel de fonction ou de méthode :  Sa "version" / son adresse est connue (c’est une fonction ou, dans le cas d’une méthode, on connaît le type dynamique de l’objet).  Son implémentation est accessible. Alors cet appel de fonction est candidat à l’inlining, c’est-à-dire au remplacement de tout le mécanisme d’appel par l’implémentation de la fonction elle-même. Le compilateur vérifie, pour chaque inlining potentiel, que la taille du code expansé ne soit pas trop supérieure à celle du code avec un appel de fonction, ce qui est généralement le cas avec les accesseurs et les modifieurs. L’inlining augmente généralement la portée des optimisation du compilateur (propagation de constantes dans le corps de la fonction, …).

Memory layout des objets simples class|struct { bool b; char c; double d; float f; int i; long l; void *ptr; char c2; bool bb[2]; int ii[3]; } a; b c &a+4 &a+8 d &a+12 &a+16 f &a+20 i &a+24 l &a+28 ptr &a+32 c2 bb &a+36 ii &a+40 &a+44 Dans les unions, tous les membres commencent à la même adresse et la taille d’une union est celle du plus grand de ses membres.

Memory layout des objets simples Les données sont disposées dans l’ordre de déclaration. Par défaut, les données sont alignées en fonction de leur taille en octets : multiple de 1 pour les bool et les char, multiple de 4 pour les int, les long, les float, les pointeurs, …, multiple de 8 pour les double. Il peut donc y avoir de l’espace inutilisé dans les structures… On peut forcer le compilateur à aligner les données différemment (packing) mais, généralement, un processeur travaillant sur un certain nombre de bits, 32 par exemple, n’aime guère les adresses qui ne sont pas des multiples de 4 octets, ou les données dont la taille ne correspond pas à 4 octets…

Héritage simple Héritage simple En cas d’héritage simple, les données sont simplement accolées (dans un ordre dépendant de l’implémentation) : class A { AAA a; }; class B: public A { BBB b; class C: public B { CCC c; C x; C:: B:: A::a b c B b= c; B:: A::a b A& a= c; Ici, l’initialisation de b par copie de c entraîne le slicing des données de c provenant de la dérivation de la classe B. a étant une référence (un "pointeur constant") vers c, aucune donnée n’est dupliquée.

Héritage simple et fonctions non virtuelles Lorsque les fonctions ne sont pas virtuelles, leur adresse est résolue lors de la compilation en fonction du type statique. class A { public: void f(); }; class B: public A { public: void f(); }; B b; b.f(); // invoque B::f() A a= b; // slicing de b a.f(); // invoque A::f() A& c= b; c.f(); // invoque A::f() bien que c soit en fait “un B”

Fonctions virtuelles Fonctions virtuelles Dès lors qu’une classe a au moins une méthode virtuelle, ses instances ont un pointeur caché ("vptr") vers la table des méthodes virtuelles ("vtbl"). Les tables de méthodes virtuelles sont partagées par toutes les instances d’une même classe. class A { XXX a; public: virtual ~A(); virtual void f(); void g(); }; A::vtbl vptr ~() B b; B:: A::a f() b b.f(); b.g(); class B: public A { YYY b; public: virtual ~B(); virtual void f(); void g(); virtual void h(); }; A& a= b; B::vtbl a.f(); ~() f() a.g(); h()

Casts de pointeur polymorphe en pointeur non-polymorphe Dans les implémentations de compilateurs C++ où le vptr est placé au début des objets, le cast d’un pointeur vers une classe ayant au moins une méthode virtuelle en un pointeur vers une classe n’ayant pas la moindre fonction virtuelle nécessite un décalage. class A { AAA a; }; class B: public A { BBB b; public: virtual void f(); B * b= new B(); vptr B:: A::a A * a= b; b Le compilateur doit tout d’abord vérifier que b ne soit pas 0/NULL, puis lui ajouter sizeof( vptr ). De même, pour pouvoir comparer a et b (==, !=), le compilateur sait qu’il faut normaliser les pointeurs. Par contre, delete a génère du code “faux” : a ne pointe plus sur un début de bloc alloué par new ! Ainsi, les casts (implicites ou non) peuvent nécessiter de générer du code…

Héritage multiple sans fonctions virtuelles class A { AAA a; }; class B: A { BBB b; class C: A { CCC c; class D: B, C { DDD d; D:: B:: A::a b C:: A::a c d D * d= new D(); C * c= d; (c == d)  true ! delete c; 

Héritage multiple avec fonctions virtuelles class A { AAA a; public: virtual ~A(); }; class B: A { BBB b; class C: A { CCC c; class D: B, C { DDD d; D:: B:: A:: vptr D * d= new D(); a b B * d_b= d; C:: A:: vptr A * d_b_a= d_b; a c C * d_c= d; d A * d_c_a= d_c; Avec, toujours : d == d_c  true ! Mais : delete d_c; 

Héritage virtuel Héritage virtuel Les données des classes héritées virtuellement ne sont pas "dupliquées". Chaque classe héritant vituellement d’une classe de base contient un pointeur vers les données de cette classe. class A { AAA a; }; class B: virtual A { BBB b; class C: virtual A { CCC c; class D: B, C { DDD d; D:: B:: A* b C:: A* c d A:: a

Le RTTI Le RTTI Lorsque le RTTI est activé, le compilateur initialise une instance de la classe type_info par classe, pour les références, plus ce qu’il faut pour tous les pointeurs rencontrés ainsi que les types primitifs. Ce sont des références vers ces instances (constantes) qui seront renvoyées par typeid. ..... Le résultat de typeid peut-être connu lors de la compilation :  Pour les types primitifs : int i; const type_info& i_ti= typeid( i );  Pour les pointeurs (seul le type statique de l’objet pointé intervient) : A * ptr= new C(); // C dérive de A const type_info& ptr_ti= typeid( ptr ); Pour les références (non triviales), le résultat de typeid est déduit du vptr.

typeid et références non triviales Pour les références non triviales, le résultat de typeid est déduit du vptr. La référence vers l’instance de type_info correspondant à la vtbl peut, par exemple, être stockée dans la cellule -1: Avant d’accéder au vptr, le compilateur insère du code visant à vérifier que la référence ne soit pas 0/NULL… -1 X& x= *(new Y()); type_info 1 Y:: vptr 2 … … Y::vtbl Tout cela est fait par une fonction dont le coût n’est pas négligeable !

Construction d’objets La construction d’un objet se fait en deux étapes distinctes : ..... 1 Allocation de l’espace mémoire : Si l’objet est alloué en pile, par simple décalage du pointeur de pile. Si l’objet est alloué dans le tas (par appel à new), par appel de l’operator new de la classe, s’il a été défini ou hérité, ou de l’operator new global. 2 Le constructeur adéquat est invoqué, ce qui peut générer une cascade d’appels aux constructeurs des classes de base. Les données seront construites dans l’ordre des déclarations (au besoin, le compilateur les réordonne avec avertissement selon le niveau de warning). Chaque entrée dans un constructeur implique la mise à jour du vptr (si vptr il y a) : attention aux méthodes virtuelles pures ! Pour les tableaux d’objets : 1 Allocation de l’espace mémoire : similaire si l’objet est alloué en pile ; dans le tas, l’allocation est faite par l’operator new[] adéquat (de classe ou global). 2 Le constructeur par défaut est invoqué de manière itérative pour initialiser chaque cellule (ce qui peut encore générer une cascade d’appels aux constructeurs des classes de base, etc.)

Destruction d’objets Destruction d’objets Inversement, la destruction d’un objet se fait en deux étapes inverses : ..... 1 Invocation ascendante des destructeurs (le vtpr, si vptr il y a, est mis à jour à chaque étage). 2 Libération de la mémoire : Si l’objet est alloué en pile, par simple décalage du pointeur de pile. Si l’objet est alloué dans le tas (par appel à new), par appel de l’operator delete de la classe, s’il a été défini ou hérité, ou de l’operator delete global. Pour les tableaux d’objets, c’est ric-rac. Rappel : attention aux classes polymorphes dont le destructeur n’est pas virtuel !!!!!

operator new et operator delete Les operator new et delete globaux ou d’une classe peuvent être redéfinis, dans le but de fournir une gestion spécifique de la mémoire : ..... #include <cstddef> // à voir … void * operator new( size_t size ); void operator delete( void * ptr ); void operator delete( void * ptr, size_t size ); void * operator new[]( size_t size ); void operator delete[]( void * ptr ); void operator delete[]( void * ptr, size_t size ); La définition des versions avec taille des operator delete sont selon votre bon vouloir… En règle générale, si vous redéfinissez operator new, redéfinissez operator delete ! Exemple d’application : allocateur mémoire rétenseur (qui ne libère jamais vraiment la mémoire), qui chaîne les blocs libérés pour les retourner à la prochaine demande d’allocation.

Placement operator new Les operator new (scalaires ou de tableaux) peuvent être surchargés, le premier argument devant être de type size_t. ..... class A { public: A( XXX, YYY ); void * operator new( size_t, WhatEver1, WhatEver2 ); void operator delete( void *, WhatEver1, WhatEver2 ); … } (idem pour les constructeurs de tableaux) De tels constructeurs sont invoqués par : new ( whatever1, whatever2 ) A( xxx, yyy ); L’operator delete correspondant, s’il a été défini, n’est appelé automatiquement que si le constructeur lance une exception.