• Aucun résultat trouvé

[PDF] Formation Programmation C++ enjeux et pratique | Cours informatique

N/A
N/A
Protected

Academic year: 2021

Partager "[PDF] Formation Programmation C++ enjeux et pratique | Cours informatique"

Copied!
32
0
0

Texte intégral

(1)

1. PRESENTATION GENERALE ... 3 1.1 Historique: ... 3 1.2 Caractéristiques : ... 3 1.3 Objectifs atteints : ... 3 1.4 Remarque ... 3 1.5 Bibliographie ... 4

2. LES EXTENSIONS DE C (NON OBJETS) ... 5

2.1 Les commentaires ... 5

2.2 Conversions explicites ... 5

2.3 Utilisation des types enum, struct et union ... 5

2.4 Déclaration des variables ... 5

2.5 Fonction inline ... 6

2.6 Paramètres optionnels avec valeur par défaut ... 6

2.7 Surcharge des noms de fonctions ... 6

2.8 Opérateurs (Objets) de sortie et d’entrée: << et >> ... 7

2.9 Opérateurs : new et delete ... 7

2.10 fonction de gestion d’erreur d’allocation... 8

2.11 Adressage par réference & ... 8

2.12 Les fonctions génériques (template) ... 9

3. INCOMPATIBILITES ENTRE C ET C++ ... 10

3.1 Entête de fonctions ... 10

3.2 Prototypes de fonctions ... 10

3.3 fonction sans argument ... 10

3.4 fonction sans valeur de retour ... 10

3.5 le type void * ... 10

3.6 Symbole de type const. ... 10

(2)
(3)

1. Présentation générale

1.1

Historique:

C++ a été développé vers 1980 par Bjarne Stroustup (Bell Labs, AT & T )

Améliorer le langage C

1.2

Caractéristiques :

Le langage C++ se présente presque comme un sur-ensemble du langage C ANSI.

 Quelques incompatibilités entre C et C++.

Le langage C++ contient des (améliorations) caractéristiques nouvelles par rapport au C qui ne sont pas spécifiquement objet.

Le langage C++ offre la possibilité de programmation par objets.

 Rien n’impose au programmeur de programmer en utilisant les concepts de la POO.

1.3

Objectifs atteints :

 C++ conserve les aspects positifs de C:

Portabilité, Concision, Efficacité, Bas niveau de langage

 Corrige les mauvais côtés de C : trop grande permissivité.

Permettre la programmation par OBJETS

1.4

Remarque

(4)

1.5

Bibliographie

Delannoy Claude Programmer en langage C++ (248 F.) Ed. Eyrolles

Delannoy Claude Apprendre le C++ sous Turbo/Borland C++ (250 F.) Ed. Eyrolles

Delannoy Claude Exercices en langage C++. Programmation orientée objets Ed. Eyrolles

Leblanc Gérard Turbo/Borland C++ (236 F.) (technique BIOS .... ) Ed. Eyrolles

O'Reilley Cd, Oualline Steve La programmation C++ par la pratique (255 F.) (exemples) Meyer Jean Jacques Borland C++ TurboC++ 3.0/3.1 pour Windows Ed. Dunod Tech Weiskamp, Heiney K. L., Flamig B. Object Oriented Programming with Turbo C++

Ed. Wiley

Borland C++ 3.0 Guide du programmeur Ed Borland

Petzold Charles Programmer sous Windows 3.1 Ed. Microsoft Press.

B Stroustup. Le langage C++ 2ième édition Ed. Addison-Wesley

Lipman S.B L’essentiel du C++ 2ième édition Ed. Addison-Wesley

Meyer B. Conception et programmation par objets pour du logiciel de qualité

Ed. InterEditions

Charbonnel Jacquelin Le langage C, les finesses d'un langage redoutable

Charbonnel Jacquelin Langage C++, les spécifications du standard ANSI/ISO expliquées (260 F.)

InterEditions (2ième édition).

Clavel G., Mirouze N., Pichon E., Soukal M. Java la synthèse Ed. InterEditions

(5)

2. Les extensions de C (non objets)

2.1

Les commentaires

C C ++

/* commentaire en C */ // commentaire en C++ et C ANSI

/* com. toujours acceptable en C++ */

Un programme est beaucoup plus souvent lu qu’il n’est écrit

2.2

Conversions explicites

Nouvelle syntaxe : (le cast est toujours valable)

C C ++ int i = 1; float x = 3.2; i = (int) x; x = (float) i; int i = 1; float x = 3.2; i = int(x); x = float(i);

Point p = Point(1.0, 2.0) ; (initialisation par

l'appel d'une fonction constructure)

i = (int) x; // toujours valable x = (float) i;

2.3

Utilisation des types enum, struct et union

Le nom d’une énumération , d’une structure ou d’une union est un type.

C C ++ enum E { ... }; struct S { ... }; union U { ... }; enum E e; struct S s, *ptr; Union U u;

Ptr = malloc( sizeof( struct S) );

enum E { ... }; struct S { ... }; union U { ... }; E e;

S s, *ptr;

struct S s1; // toujours valable U u;

Ptr = malloc( sizeof( S ) );

C C ++

typedef enum { False, True } BOOL; BOOL ouvert ;

enum BOOL { False, True }; BOOL ouvert;

2.4

Déclaration des variables

 On peut déclarer des variables en tout endroit dans une bloc et non plus uniquement avant la première instruction du bloc.

(6)

{

int i; // déclaration de variables i = 2; // instruction

int j; // autre déclaration de variable j = i;

...

for( int k = 0; k < 5; k++ ) { ...

}

// valeur de k ici ? (dépend des compilateurs !) }

2.5

Fonction inline

 Fonction expansée (développée) à chaque appel

inline int carre(int x) { return x*x; } int j = carre( 2 ); // j vaut 4

Ressemble à une macro C mais faire très attention dans la macro (effet de bord) !

C C ++

#define ABS(x) (x)>0 ? (x) : (-x)

int i, k = -1;

i = ABS( k ) ; // i vaut 1;

inline int abs( int x ) { return x>0 ? x : -x ;} int i, k = -1;

I = abs( k ) ; // i vaut 1;

En C on utilise des macros expansés par le préprocesseur.

Inconvénient : parfois difficile à écrire, pas de contrôle de type, mise au point difficile.

2.6

Paramètres optionnels avec valeur par défaut

void f ( float, int = 2, char * = " ") ; int g ( int *ptr = null , char ); // illégal

f ( 1.23, 10, " bonjour "); // appel classique f ( 1.23, 10 ); // => f ( 1.23, 10, " "); f ( 1.23 ); // => f ( 1.23, 2, " ");

f ( ) // illégal

f ( 1.23, , " bonjour "); // => illégal f ( 1.23, " bonjour " ); // => illégal

 La définition des valeurs par défaut peut se faire soit dans le prototype de la fonction, soit dans l’entête de sa définition, mais pas dans les 2 à la fois. Mettre de préférence dans le prototype.

 Les valeurs par défaut sont placées à partir de la fin de la liste des paramètres.

2.7

Surcharge des noms de fonctions

Surcharge ou encore surdéfinition : plusieurs fonctions ont le même nom.

(7)

int max(int, int );

int max( const int * list );

// mais erreur car même signature et valeur retournée différente char * cherche( char *);

int cherche( char * );

2.8

Opérateurs (Objets) de sortie et d’entrée: << et >>

Ces opérateurs sont en réalité des opérateurs relevant des possibilités OBJET du langage.

Ils sont introduits ici pour pouvoir les utiliser tout de suite sans attendre tous les développements nécessaires (et longs) à leur explication.

#include <stdio.h> #include <conio.h>

#include <iostream.h> // pour pouvoir utiliser les flots cin et cout int main()

{

int n; float x;

// méthode traditionnelle avec printf et scanf printf( "\n\nentrez un entier et un flotant : ");

scanf( "%d %f", &n, &x );

printf( " le produit de %5d par %10.2f est : %10.2f\n", n, x, n * x ); // méthode C++ avec les flots cin et cout

cout << "\n\nentrez un entier et un flotant : " ; cin >> n >> x ;

cout << " le produit de " << n << " par " << x << " est " << n * x ; return 0;

}

<< est appelé output on insère dans le flot de sortie (opérateur d'insertion ou injection) >> est appelé input on extrait du flot d'entrée (opérateur d'extraction)

(comme un entonnoir !)

<<endl; // équivaux à /n mais plus performant en objet 4 flots prédéfinis :

cin (équivalent stdin en C) cout (équivalent stdout en C) cerr (équivalent stderr en C) clog

2.9

Opérateurs : new et delete

Utiliser les opérateurs new et delete de préférence à malloc et free. syntaxe :

pointeur = new type ; delete pointeur ;

(8)

C C ++ int *ptr, *tab;

ptr = (int *)malloc( sizeof( int ) ); tab = (int *)malloc( n * sizeof( int ) ); ...

free( tab ); free( ptr );

int *ptr, *tab; ptr = new int; tab = new int[ n ]; ...

delete ptr;

delete [ ] tab ; // ou delete tab;

2.10

fonction de gestion d’erreur d’allocation

On peut définir une fonction qui sera appelée automatiquement si l’opérateur new échoue.

typedef void (*HANDLER)() ;

HANDLER HandlerPrecedent = set_new_handler( memoireSaturee ); void memoireSaturee()

{

cout << " memoire saturee " ; exit( 1 );

} void f( ) {

HandlerPrecedent = set_new_handler( memoireSaturee ); ....

set_new_handler(HandlerPrecedent); // restauration }

2.11 Adressage par réference &

int val = 10

int *pval = &val // initialisation non obligatoire

*pval = 5 ; *pval += 2;

int val = 10;

int &refVal = val;// pas int &refVal = &val;

// initialisation obligatoire

refVal = 5; // maintenant val vaut 5 refVal += 2; // maintenant val vaut 7 // identique à val += 2 ;

 Nouveau type.

Si T est un type, T& est le type référence vers T

 Toutes les manipulations de la référence s’effectuent sur l’objet référencé.

 Une référence doit obligatoirement être initialisée lors de sa déclaration.

Une référence est un alias (un autre nom) pour la variable référencée.

 On ne peut pas définir un pointeur sur une référence.

5 100 7 100 val 100 refVal val refVal

refVal += 2;

15 104 7 104 j 106 102 104 refj j refj

refj = refVal;

(9)

2.11.1 Utilisation dans les passages de paramètres

 Equivalent au VAR du langage PASCAL

 Syntaxe beaucoup plus lisible et naturelle.

C C ++

void echange( int *x, int *y) { int temp;

temp = *x; *x = *y; *y = temp; }

void main() { nt a = 1, b = 2; change( &a, &b ); }

void echange( int &x, int &y) { int temp; temp = x; x = y; y = temp; } void main() { int a = 1, b = 2; echange( a, b ); }

2.11.2 Retour d’une fonction par reference

C C ++

int x, y ...

int *f( int b ) { return b ? &x : &y; } ....

... Possible mais trop complexe, à éviter !

int x, y ...

int &f( int b ) { return b ? x : y ; } ....

int i = f( 1 ); // range x dans i f ( 0 ) = 5; // range 5 dans y

on peut utiliser ainsi une fonction dans la partie gauche d’une expression

2.12 Les fonctions génériques (template)

création d'un modèle (d'un moule, d'un exemple) de fonction. Le compilateur créera la fonction réelle quand ce sera utile. Voir plus loin.

(10)

3. Incompatibilités entre C et C++

3.1

Entête de fonctions

La syntaxe Kernigham & Ritchie n’est plus acceptée

void echange( x, y) // erreur de compilation

int &x, &y; { ... }

3.2

Prototypes de fonctions

Le nombre et le type des paramètres de fonctions sont contrôlés à chaque appel.

Tout appel de fonction doit donc obligatoirement être précédé d’un prototype ou de la définition de la fonction. (Comme en C ANSI)

3.3

fonction sans argument

float f1 ( ) ; // fonction sans argument et pas float f ( void);

3.4

fonction sans valeur de retour

void f2 ( ) ; // ni f2 ( ) ni void f2( void ) ;

3.5

le type void *

la conversion implicite n’est possible que dans le sens void *  Type *

void *generic; int *ptr;

generic = ptr; // ok

ptr = generic; // erreur à la compilation

ptr = ( int *)generic; // ok

3.6

Symbole de type const.

Le mot clé const, placé devant un symbole, est un modificateur qui signifie que la valeur du symbole ne doit pas être modifiée.

En C++ un symbole défini avec const est local au fichier : on peut redéfinir le même symbole dans un autre fichier (avec une valeur différente)

en C il y a une erreur à l'édition de liens.

en C++, const est utilisé en remplacement de la directive #define du préprocesseur.

En C en C++

#define MAX 100 char tab[ MAX + 1]

const int max = 100; char tab[ MAX + 1]

 En C++ une constante doit obligatoirement être initialisée lors de sa déclaration.

4. CONCEPTS DE LA PROGRAMMATION ORIENTEE OBJET : LES CLASSES ET LES

(11)

4.1 Programmation traditionnelle en C... 13

4.2 Programmation objet : l'encapsulation. ... 15

4.3 Définitions. ... 15

4.4 Améliorations 1 : private, accesseur. ... 17

4.5 Amélioration 2 : Fonctions membres constructeur et destructeur ... 19

4.6 Autres éléments d'une classe : ... 21

4.7 Définition et utilisation des objets ... 22

4.8 Organisation pratique des fichiers ... 23

4.9 exercice : ... 24

5. CONSTRUCTION DES OBJETS ... 25

5.1 Classe de mémorisation des objets. ... 25

5.2 Constructeur par défaut. ... 25

5.3 Constructeur de recopie. ... 27

5.4 Cas des objets avec des objets membres. ... 29

5.5 initialisation des objets ( <> affectation ). ... 29

5.6 Affectation d’objets. ... 29

5.7 Tableaux d’objets. ... 30

6. S AMIS. ... 31

6.1 Fonction amie. ... 31

(12)
(13)

4. Concepts de la programmation orientée objet : les classes et

les objets

4.1

Programmation traditionnelle en C

soit le programme suivant :

#include <iostream.h>

struct Personne { // une personne est décrite par son nom et sa societe char nom[ 20 ] ;

char societe[ 30 ]; } ;

void presente( struct Personne p )

//--- // affiche la description d'une personne

{

cout << "je m'appelle " << p.nom << endl ; cout << "je travaille à " << p.societe << endl ; }

int main() {

// on definit une variable individu qui décrit une personne struct Personne individu ; // struct inutile en C++ // on initialise cette variable

strcpy( individu.nom, "Durand" ); strcpy( individu.societe, "Thomson" );

// on affiche la description de la variable individu presente( individu );

return 0 ; }

Cet exemple montre l'implémentation et l'utilisation d'un type Personne.

(14)

5. séparation des données (nom et société) et des

traitements (presente() ) Le type abstrait Personne est

aussi bien caractérisé par ses données et ses

traitements.

 Les contrôles sont difficiles à assurer : on peut afficher une personne sans avoir donné une valeur à ses données (variable non initialisée), on peut changer le nom d'une personne (toutes les données sont accessibles à l'utilisateur de la structure) !...

(15)

6. Programmation objet : l'encapsulation.

Cahier des charges d'un type Personne :

 Une personne est décrite par 2 informations :

son nom

la société où elle travaille.

 Une personne est capable de se présenter en affichant ses données.

class Personne { // une personne est décrite par son nom, sa societe, son comportement

public :

char nom[ 20 ] ; char societe[ 30 ]; void presenteToi( ) {

cout << "je m'appelle " << nom << endl ; cout << "je travaille à " << societe << endl ; }

} ;

int main() {

// on definit une variable individu qui décrit une personne Personne individu ;

// on initialise cette variable strcpy( individu.nom, "Durand" ); strcpy( individu.societe, "Thomson" ); // on demande à individu de se présenter individu.presenteToi( );

return 0 ; }

Les changements :

Les données et les comportements sont rassemblés - encapsulés - dans une même entité. L'objet est défini par ses données et par son comportement.

La fonction presenteToi() n'a pas besoin de paramètres. Les champs nom et societe sont directement accessibles à l'intérieur de la classe.

L'instruction : individu.presenteToi( ); représente l'envoi d'un message à l'objet individu. Cet envoi de message déclenche le traitement demandé. Ce traitement est connu de l'objet.

6.1

Définitions.

Une classe est analogue à un type (extension du type struct du C).

Un objet est une instance (une entité identifiable, un exemplaire) d’une classe. Un objet est analogue à une variable. La création d'un objet est appelée une instanciation.

Une classe comprend

des données (les données membres, des champs). Les variables sont des variables d'instances 1. des fonctions membres pour manipuler les données membres. Les fonctions membres décrivent le

comportement des objets. (Les méthodes).

l’encapsulation:

Regroupement dans une même structure des données et du comportement (fonctions qui manipulent ces données).

(16)

class NomClasse // nom de la classe {

public : // spécificateur d'accès

int x, y ; // variables membres

char s[ 10 ] ;

void f( ) ; // fonctions membres

int g( ) ; } ;

Une classe se définie comme une structure.

A partir d'une classe on peut créer autant d'objets que nécessaire. Chaque objet contient ses données membres (variables d'instances), mais le comportement n'est défini qu'une seule fois dans la classe pour tous les objets membres de la classe.

Pour déclencher un traitement, un comportement, il faut envoyer un message à un objet.

Les membres d'un objet ont un spécificateur d'accès : ici public. Public veut dire que tous les membres sont accessibles de l'extérieur de la classe.

p1, un objet Personne La classe Personne deux instanciations societe nom cout << ... ; cout << ....; presenteToi() Durand nom Thomson societe

p2, un autre objet Personne

Dupond

nom

ISAIP

(17)

6.2

Améliorations 1 : private, accesseur.

Mais Les contrôles sont toujours difficiles à assurer : on peut afficher une personne sans avoir donné une valeur à ses données (variable non initialisée), on peut changer le nom d'une personne (toutes les données sont accessibles à l'utilisateur de la structure) !...

Nouveau cahier des charges d'un type Personne :

1. Une personne est décrite par 2 informations : son nom et sa société

2. Une personne a toujours un nom, ce nom ne peut pas changer, tous les caractères sont en majuscule, il comporte 20 caractères max.

3. Une personne peut ne pas être associée à une société (non salariée ).

4. S'il existe, le nom de la société comporte 30 caractères max et est en majuscule. 5. Une personne peut changer de société.

6. Une personne est capable de se présenter en affichant ses données.

Nouveau spécificateur d'accès : private :

Les membres d'accès privés sont inaccessibles en dehors de la classe. Il ne sont accessibles que par les membres de la classe.

Pour pouvoir accéder aux membres privés il faut ajouter des fonctions membres publics.

class Personne { // une personne est décrite par son nom et sa societe private :

char nom[ 20 + 1 ] ; // membres privés

char societe[ 30 + 1 ] ; char *majuscule( char * ) ;

public : // membres publics

void initNom( char *nm )

{ strncpy( nom, majuscule( nm ), 20 ) ; } void initSociete( char *soc )

{ strncpy( societe, majuscule( soc ), 30 ) ; } void init( char *nm, char *soc = "?" ) ;

void presenteToi() ; } ;

#include <iostream.h> #include <ctype.h> #include <string.h>

char *Personne :: majuscule( char *texte ) //--- {

int longueur = strlen( texte ) ;

for( int i = 0; i < longueur; i++ ) texte[ i ] = toupper( texte[ i ] ) ; return texte ;

}

void Personne :: init( char *nm, char *soc ) //--- // initialise les membres de la personne

// mets les noms en majuscule

// initialise par défaut la société à ? si pas défini {

initNom( nm ); initSociete( soc ); }

(18)

void Personne :: presenteToi( )

//--- {

cout << "je m'appelle " << nom << endl ; if( strcmp( societe, "?" ) != 0 )

cout << "je travaille à " << societe << endl ; else

cout << "je ne suis pas dans une societe" << endl ; }

int main()

//======================================================== {

Personne individu ;

individu.init( "DuPond", "Renault" ); individu.presenteToi();

individu.init( "Durand" );

// strcpy( individu.nom, "Durand" ); est maintenant impossible individu.presenteToi();

return 0 ; }

6.2.1 remarques :

 On peut trouver des variables membres et des fonctions membres privées.

Les membres d’une classe peuvent être des variables, des constantes, des fonctions. des éléments de type prédéfini ou défini par l’utilisateur, d’autres objets, des déclarations de type ...

Les membres d’une classe peuvent être privés ou publics. (private, public)

privés : ils ne sont connus et utilisables directement que par les objets de la classe publics : ils sont connus et utilisables par tout utilisateur

 Par défaut les membres sont privés.

 Les fonctions privées sont réservées à un usage interne à la classe.

 Les fonctions peuvent être

soit déclarées dans la classe et définies en dehors de la classe, soit déclarées et définies en même temps dans la classe.

 :: opérateur de (résolution de) portée. Pour préciser à quelle classe appartient la fonction.

Les fonctions définies dans la classe sont inline par défaut. Elles sont en général petites. En général on définit à l'intérieur les fonctions membres très petites ( une ligne ).

 Toutes les fonctions membres d’un objet donné de la classe ont accès à tous les membres de cet objet (données ou fonctions)

initNom()

(19)

L'abstraction des données : La structure exacte d'une donnée est cachée. On peut utiliser un objet sans connaître sa structure interne.

Les données privées forment l'implémentation de la classe les données publiques constituent l'interface entre l'objet et les utilisateurs 6.2.2 Accesseur ( sélecteur, manipulateur ).

Accesseur : fonction membre publique qui permet d'accéder aux variables d'instances privées.

Une variable d'instance privée peut :

n'avoir aucun accesseur : c'est une variable d'implémentation dont l'existence n'est pas forcément connue par l'utilisateur qui ne peut ni la consulter ni la modifier.

Avoir un accesseur en consultation : l'utilisateur de la classe peut consulter la variable d'instance mais ne peut pas la modifier.

Avoir deux accesseurs, l'un en consultation, l'autre en modification : l'utilisateur de la classe peut consulter la variable d'instance et la modifier.

class Personne { private :

char nom[ 20 + 1 ] ; // membres privés

char societe[ 30 + 1 ] ; char *majuscule( char * ) ;

public : // membres publics

void initNom( char *nm ) { strncpy( nom, majuscule( nm ), 20 ) ; } void initSociete( char *soc ) { strncpy(societe, majuscule( soc ), 20 ) ; } void init( char *nm, char *soc = "?" ) ;

void presenteToi( ) ;

char *sonNom( char *nm) { return strcpy( nm, nom ) ; } } ;

class Complex {

double x, y; public :

void init( double xx, double yy ) ; // accesseur en modification

double &X( ) { return x; } // accesseur en consultation et modification double &Y( ) { return y ; }

double module() { return sqrt( x*x + y*y ) ; } } ;

6.3

Amélioration 2 : Fonctions membres constructeur et destructeur

6.3.1 fonctions membres constructeur.

class Personne { private :

char nom[ 20 + 1 ] ; // membres privés

char societe[ 30 + 1 ] ; char *majuscule( char * ) ;

void initNom( char *nm ) { strncpy( nom, majuscule( nm ), 20 ) ; }

public : // membres publics

Personne( char *nm, char *soc = "?" ) ; // fonction constructeur

void initSociete( char *soc ) { strncpy( societe, majuscule( soc ), 20 ) ; } void presenteToi() ;

char *sonNom( char *nm) { return strcpy( nm, nom ) ; } } ;

(20)

Fonction membre appelée automatiquement juste après toute création d’un objet. (après l’allocation de l’espace mémoire destiné à l’objet)

Possède le même nom que la classe.

N’a pas de type de retour.

 Le constructeur permet d'initialiser un nouvel objet.

 Il peut servir à initialiser l’objet au moment de sa création. (c’est sa fonction principale pour le programmeur).

 On peut définir plusieurs constructeurs. Les valeurs d’initialisation sont mentionnées :

1. entre parenthèses après le nom de l’objet lors de sa définition. 2. en utilisant le signe = suivi explicitement de l’appel du constructeur

3. en utilisant le signe = suivi d’une valeur d’initialisation si le constructeur ne peut accepter qu’un seul paramètre.

Personne p1 ("Durand", "Renault" );

Personne p2 = Personne("Durand", "Renault");

Personne p3( "Durand" ); // équivalent à p3("Durand", "?") Personne p4 = Personne("Durand" ); // équivalent à p4("Durand", "?")

Personne p5 ; // illégal pas de constructeur sans paramètre

Personne p6 = "Durand"; // équivalent à p6("Durand", "?") quand il y aun seul paramètre

Même principe pour des objets dynamiques:

Complex *z1 = new Complex; // illégal Complex *z2 = new Complex(1, 2);

 On peut définir plusieurs constructeurs avec des paramètres différents.

6.3.2 Fonction membre destructeur

class Tableau {

int *ptr; public :

Tableau ( ) { ptr = new int [ 100 ] ; } ~Tableau ( ) { delete [ ] ptr ; }

} ;

Fonction membre appelée automatiquement juste avant toute destruction d’objet. (Avant la libération de la mémoire allouée).

Possède le même nom que la classe précédée de ~ (tilda) (alt 126).

N’a ni paramètre ni type de retour.

Une classe ne peut posséder qu’un seul destructeur.

Un destructeur sert principalement à nettoyer la mémoire quand l’objet possède des données allouées dynamiquement.

(21)

6.4

Autres éléments d'une classe :

6.4.1 Membres constants

class Tableau {

const int taille; // pas d'initialisation ici int *ptr;

public :

Tableau ( int n ) : taille( n ) { ptr = new int [ taille ] ; } ~Tableau ( ) { delete [ ] ptr ; }

} ;

L’initialisation d’un membre constant se fait sur l’entête de chaque constructeur.

 Il s'agit d'une constante pour chaque objet, pas pour la classe. 6.4.2 Fonctions constantes : fonctions de consultation

class Complex {

double x, y; public :

Complex( double xx, double yy ) ; double X( ) const { return x ; } double Y( ) const { return y ; } void annule( ) { x = y = 0 ; } } ;

Ce sont des fonctions qui ne modifient pas l’état d’un objet : fonctions de consultation ( ou accesseur de

consultation ).

on obtient

const Complex z( 1, 2 ); double x = z.X( );

z.annule( ) ; // erreur à la compilation.

 sur un objet constant on ne peut appeler que des fonctions constantes. 6.4.3 Membres statiques

Class Point {

static int nbPoint; int x, y;

public :

Point( int xx, int yy ) { x = xx; y = yy; nbPoint++ ; } ~Point( ) { nbPoint-- ; }

... } ;

int Point :: nbPoint = 0; // déclaration obligatoire à l’extérieur de la classe // en global et initialisation

Membres partagés par tous les objets d’une même classe : variables globales à la classe.  initialisation au niveau global, pas dans un contructeur pour un objet donné.

(22)

6.4.4 Fonctions statiques

Class Point {

static int nbPoint; int x, y;

public :

Point( int xx, int yy ) { x = xx; y = yy; nbPoint++ ; } ~Point( ) { nbPoint-- ; }

static int litNbPoint ( ) { return nbPoint ; } ...

} ;

 De la même façon une fonction statique est indépendante de tout objet.

 Une fonction statique est une fonction de classe.

 Une fonction statique n’a accès qu’aux membres statiques ( de classe ).

 Un objet a accès à ses membres propres et aux membres statiques.

int nb = Point :: litNbPoint ( ) ; // de préférence int nb = objetPoint.litNbPoint( ) ; // moins naturel

 Les fonctions de classe (publiques) ne dépendant pas d’un objet particulier peuvent être appelées même si aucun objet n’a encore été créé.

6.4.5 Le pointeur this

Toute fonction membre (non statique) dispose implicitement d’un pointeur sur l’objet courant

manipulé par la fonction : ce pointeur est nommé this.

De type const C * const

class Complex {

int x, y; public :

int &X() { return x ; } // équivalent à { return this->x ; } int &Y() { return y ; } // équivalent à { return this->x ; } Complex( int x, int y ) { this->x = x; this->y = y ; }

void affiche() { cout << "x = " << x << "y = " << y

<< " adresse de la variable = " << this ; } } ;

ici this est de type Complex *

6.5

Définition et utilisation des objets

Les objets sont des variables du type de la classe, ce sont des instances de la classe. Les objets se définissent et se manipulent comme des variables de type structure. Definition = instanciation.

Complex z, *pz; // les membres de z sont définis par le constructeur double x, y, rho;

x = z.X( ) ; y = z.Y( ) ;

rho = pz->module( ); // ou (*pz ).module( ) ; à éviter

(23)

class Rectangle {

unsigned h, l; // membres privés

unsigned c; // couleur void modifie( int, int ); public :

Rectangle( unsigned haut = 0, unsigned larg = 0, unsigned col = NOIR ); unsigned hauteur( );

unsigned largeur ( ); unsigned couleur ( ); void dessine ( ); void aggrandit( int ) void deplace( int , int ) } ;

Rectangle r;

unsigned hauteur, largeur, couleur;

largeur = r.l ; // erreur à la compilation

largeur = r.largeur();

r.h = 10; // erreur à la compilation

r.modifie( 5, 8 ) ; // erreur à la compilation r.deplace( 5, 8 );

On peut faire des affectations entre objets comme entre des structures. Il y a alors recopie des valeurs de tous les champs de données.

Les fonctions ne sont pas copiées, elles sont définies une seule fois et appartiennent à tous les objets de la classe.

Complex z1, z2;

z1 = z2; ; z1 et z2 ont les mêmes valeurs pour x et y

6.6

Organisation pratique des fichiers

on aura en général :

 un fichier entête .h pour chaque définition de classe

 un fichier source .cpp pour la définition des fonctions publiques de chaque classe.

 Les fichiers d'application .cpp qui utilisent les classes.

un projet contenant les fichiers d'application .cpp et les fichiers .cpp des classes.

#ifndef RECTANGLE_H // fichier rectangle.h :

#define RECTANGLE_H class Rectangle

{

int x0, y0, x1, y1 ; // membres privés

unsigned c; // couleur

void modifie(int x0, int y0, int x1, int y1 ); public :

Rectangle( int x0, int y0, int x1, int y1, unsigned col = NOIR ); unsigned hauteur( ) { return x1 - x0 ; }

unsigned largeur ( ) { return y1 - y0 ; } unsigned couleur ( ) { return c ; } void dessine ( ) ;

void aggrandit( int dx, int dy ) ; void aggrandit( float taux ) ; } ;

(24)

#include "rectangle.h" // fichier rectangle.cpp (extrait)

void Rectangle::dessine( ) { ...

}

void Rectangle :: aggrandit( int dx, int dy ) { ...

modifie( .... ) ; }

#include "rectangle.h" // fichier appli.cpp int main()

{

Rectangle r( 10, 50, 20, 150 ); unsigned hauteur, largeur, couleur; longueur = r. largeur();

r.aggrandit( 2, 5 ); }

le projet contient les fichiers applic.cpp et rectangle.cpp

6.7

exercice :

réaliser une classe Point permettant de manipuler un point du plan.

 Les coordonnées du point seront privées.

 le constructeur recevra en argument les coordonnées du point (float)

une fonction membre déplace() permettra d'effectuer une translation du point.

Une fonction membre affiche() affichera les coordonnées du point.

Ecrire un programme d'essai qui déclare un point, l'affiche, le déplace et l'affiche à nouveau. Compléter la classe pour que la fonction affiche indique aussi le nombre d'objets de type point.

(25)

7. Construction des objets

7.1

Classe de mémorisation des objets.

Différentes classes de mémorisation des objets :

Objets automatiques (locaux): déclarés localement dans un bloc et recréés à chaque appel de la fonction Objets statiques (globaux) : déclarés en dehors de toute fonction : créés avant main(), détruits après. Objets dynamiques : créés par new et supprimés par delete.

Objets temporaires (ils sont sans nom): créés pour mémoriser un résultat intermédiaire dans le calcul d’une expression, passage de paramètres par valeur, retour de fonction par valeur.

Point p1( 2, 2 ); // objet statique void f ( Point p )

{ ...

Point p2( 0, 0 ); // objet automatique, constructeur de p2 appelé ici ...

point *ptr ; // pas d'appel de constructeur ici ...

ptr = new Point( 1, 1); // objet dynamique, constructeur appelé ici ...

delete ptr; // destructeur de l’objet dynamique appelé ici ...

} // destructeur de p2 appelé ici

// le point p1 est construit avant d’entrer dans main() int main ()

{ ...

f ( Point( 3, 3 ) ); // création d’un objet temporaire

// destruction de l’objet temporaire (au plus tôt) ...

}

// destruction du point p1

7.2

Constructeur par défaut.

S’il n’y a pas de constructeur, le compilateur génère un constructeur par défaut sans paramètre.

Le constructeur par défaut, sans paramètre, permet de créer des objets non initialisés.

De façon générale un constructeur par défaut est un constructeur qui peut être appelé sans paramètre. (Soit il n'existe aucun paramètre, soit tous les paramètres ont des valeurs par défaut).

 Si un ou plusieurs constructeurs sont définis par la classe, le constructeur pas défaut n’est pas généré.

class Texte1 {

int taille; char *ptr; public :

... // un constructeur par défaut est généré } ;

Texte1 t; // ok class Texte2 {

(26)

char *ptr; public :

Texte2 ( char * ); // pas de constructeur par défaut généré } ;

Texte2 t; // illégal

Texte2 t ( " je suis le contenu du texte " ); class Texte3 { int taille; char *ptr; public : Texte3 ()

{ taille = 80; ptr = new char[ taille + 1 ] ; } // constructeur par défaut Texte3 ( char * );

(27)

7.3

Constructeur de recopie.

7.3.1 Problème à résoudre

Class Tableau {

int *ptr;

int nb; // taille du tableau public :

Tableau ( int n ) { ptr = new int[ nb = n ]; } ~Tableau () { delete [] ptr ; } ... } ; int main() { Tableau t1( 3 ); Tableau t2 ( t1 ) ; ... return 0; }

Le tableau t2 est créé à partir du tableau t1. Le compilateur fait une copie automatique de tous les champs de t1 dans t2. Les champs sont recopiés mais pas les zones pointées.

On constate que le tableau d'entiers alloué en mémoire est référencé par deux objets différents.

A la destruction des objets t1 et t2, cette zone allouée unique est libérée deux fois par le destructeur de t1 et t2, ce qui provoque une erreur à l'exécution.

Il faudrait obtenir :

les deux tableaux t1 et t2 sont indépendants, la zone allouée pointée pour t2 est une copie de la zone allouée pour t1.

Il faut créer un constructeur de recopie.

7.3.2 définition.

Son prototype est de la forme :

Classe( Classe & ) ou mieux Classe( const Classe & )

sert à créer des clones, c’est à dire à dupliquer les objets.

class Complex {

t1

3

t2

3

t1

3

t2

3

copie

(28)

int x, y ; public :

Complex( int x, int y ) ; // constructeur 'normal'

Complex ( const Complex &z ) // constructeur de recopie (inutile ici) { x = z.x; y = z.y; }

} ;

7.3.3 Utilisations :

Appel lors de l’initialisation d’un objet par un autre objet de la classe

Complex z1( 2, 4 );

Complex z2( z1 ); // appel du constructeur de recopie Complex z3 = z1 ; // appel du constructeur de recopie

Appel lors du passage d’objet en paramètres par valeur à une fonction pour recopier le paramètre effectif dans le paramètre formel.

int module( Complex z ); // prototype de fonction (pas une fonction membre) Complex z( 1, 2 );

int m = module( z ); // appel du constructeur de recopie // pour le passage de paramètre par valeur

Appel lors du renvoi d’un objet par une fonction pour recopier la valeur de retour dans un objet temporaire :

Complex racine( ... ); // prototype de fonction(pas une fonction membre) Complex z = racine( ...); // appel du constructeur de recopie

// pour le retour de la fonction

Class Tableau {

int *ptr;

int nb; // nombres d’éléments public :

Tableau (int n) { ptr = new int[ nb = n ]; } Tableau ( const Tableau &t );

~Tableau () { delete [] ptr ; } ...

} ;

Tableau :: Tableau ( const Tableau &t ) {

nb = t.nb;

ptr = new int [ nb ] ;

memcpy( ptr, t.ptr, nb*sizeof( int ) ) ; }

Tableau t1; Tableau t2 ( t1 ) ;

Point Point :: symetrique() {

Point p ; // création d'un point automatique (local) p.x = -x; p.y = -y ;

return p; // recopie dans un point temporaire // appel constructeur de recopie }

(29)

Point &Point :: symetrique() {

Point p ; // création d'un point automatique (local) p.x = -x; p.y = -y ;

return p; // erreur à la compilation : référence sur un objet temporaire }

Point *Point :: symetrique() {

Point p ; // création d'un point automatique (local) p.x = -x; p.y = -y ;

return &p; // pas d'erreur déclarée !!!! }

// faire Point *p = new Point ; ... return p ; est aussi une mauvaise solution

Point p1( 2, 5 ), p2 = p1.symetrique() ;

7.4

Cas des objets avec des objets membres.

Dans un constructeur, il y a d'abord construction des membres (donc appel des constructeur par défaut des objets membres) puis exécution des instructions du bloc du constructeur.

Les objets membres sont donc d'abord construit avant l’objet englobant (et récursivement). Donc :

 Si les objets membres disposent d’un constructeur par défaut, celui-ci est appelé avant que le constructeur de l’objet englobant soit lui-même appelé.

Si les objets membres ne disposent pas d’un constructeur par défaut, il faut faire un appel explicite au

constructeur souhaité.

 L'appel des constructeurs a lieu en dehors du bloc du constructeur.

 Même raisonnement avec tous les constructeurs ( constructeur de recopie compris ).

class Point; class Segment {

Point a, b; public :

Segment( int, int, int, int ); ...

}

Segment :: Segment( int x1, int y1, int x2, int y2 ) : a( x1, y1 ), b( x2, y2 ) { ...

}

7.5

initialisation des objets ( <> affectation ).

L'initialisation d'un objet est toujours faite par un constructeur. Il y a initialisation d’un objet dans les 3 cas suivants:

1 - Déclarations d’un objet avec intialisation : appel d'un constructeur.

Point a = 5 ; // ou a( 5 ) il doit exister un constructeur à un argument entier Point b = a ; // ou b( a ) il doit exister un constructeur à un argument Point

2 - Transmission d’un objet par valeur, en argument d’un appel d’une fonction : appel d'un constructeur de recopie.

3 - Transmission d’un objet par valeur, en valeur de retour d’une fonction : appel d'un constructeur de recopie.

7.6

Affectation d’objets.

L’opérateur d’affectation est le signe =.

Exécuté avant les

instructions du

constructeur

(30)

Il recopie tous les membres de l’objet source dans ceux de l’objet destination.

Il faudra définir un opérateur d’affectation personnalisé dès que l’objet contient des pointeurs vers une zone dynamique.

Cas des objets membres : L’opérateur d’affectation par défaut utilise l’opérateur d’affectation pour chacun des objets membres.

Cas des pointeurs membres : Les membres de type pointeur sont copiés, mais pas les zones pointées.

7.7

Tableaux d’objets.

Il est possible de construire un tableau d’objets d’une classe donnée, à condition que la classe possède un constructeur par défaut (ou de les initialiser explicitement). Les éléments du tableau sont alors initialisés avec ce constructeur.

class Point { ....

public :

Point ( int, int ); }

Point tab[ 10 ] ; // illégal class Point

{ .... public :

Point ( int, int = 0 ); }

Point tab[ 5 ] = { Point( 1, 2), 3, Point( 4 ), 9, 6 } ;

// crée le tableau { ( 1, 2), ( 3, 0), ( 4, 0 ), ( 9, 0) ( 6, 0 ) } // mais il est impossible d'initialiser un tableau dynamique

(31)

8. s amis.

8.1

Fonction amie.

Une fonction amie d’une classe A est une fonction, n’appartenant pas à la classe A, autorisée à accéder aux membres non publiques de la classe A.

Le concepteur de la classe doit déclarer dans la classe la fonction amie avec le mot clé friend.

class Point {

int x, y; public :

friend void affiche( const Point &p );

Point( int abs = 0 ; ord = 0 ) { x = abs; y = ord ; } ...

} ;

void affiche ( const Point &p ) {

cout << " abscisse = " << p.x << " ordonnée = " << p.y ; }

Une fonction amie d’une classe A peut appartenir à une autre classe B.

class Fenetre ; class Point {

int x, y; public :

friend void Fenetre :: affiche( const Point &p ); Point( int abs = 0 ; ord = 0 ) { x = abs; y = ord ; } ...

} ;

8.2

Classe amie.

Une classe B est amie d’une classe A. Toutes les fonctions membres de la classe B sont amies de A.

classe Fenetre ; class Point { ...

friend class Fenetre ; ... } ;

(32)

Figure

Tableau :: Tableau ( const Tableau &amp;t )  {

Références

Documents relatifs

A mandioca pode cumprir um papel ecológico importante na preservação dos siste- mas tradicionais de galinha caipira, desempenhando múltiplas funções. A partir de sua Introdução

L’archive ouverte pluridisciplinaire HAL, est destinée au dépôt et à la diffusion de documents scientifiques de niveau recherche, publiés ou non, émanant des

Par exemple, si nos stratégies visuelles sont développées à partir de l’observation d’expressions faciales spontanées, les divergences entre l’information utilisée pour

La troisième entrée est ainsi sociale : l’activité de cueillette des produits forestiers non ligneux concerne les populations habitant en forêt ou en lisière de

On dispose de données sur le niveau de scolarité des de famille, d'où il ressort que les les moins celles qui correspondent au rural profond, se dans le Alto

La présente thèse démontre que la campagne napoléonienne de 1815 n'est pas entreprise pour des motifs exclusivement stratégiques, mais aussi pour répondre à des

Ce à quoi l’on assiste est en fait une restructuration de l’État, à un changement en profondeur de ses modes d’intervention dans la société, ceci pour répondre à une

L’archive ouverte pluridisciplinaire HAL, est destinée au dépôt et à la diffusion de documents scientifiques de niveau recherche, publiés ou non, émanant des