Cours complet C++ pas à pas
![Cours complet C++ pas à pas](/images/remos_downloads/CoursInformatique-id3045.3045.pdf_thumb_prev.jpg)
INTRODUCTION
Ce cours suppose le langage C connu.
On trouvera le listing du corrigé des exercices à la fin de chaque chapitre.
Une disquette, contenant le programme source de ces exercices est fournie avec le polycopié.
Ces programmes ont été mis au point sous environnement BORLAND C++ sous WINDOWS.
Pour avancer un peu plus vite et aborder l’essentiel de la Programmation Orientée Objet (P.O.O.), on pourra étudier les chapitres et paragraphes marqués de ***, dans un deuxième temps.
En principe, selon le principe de la compatibilité «ascendante», tout programme écrit en C fonctionne sous un environnement C++.
Bibliographie:
- Le C++ sous TURBO BORLAND C++ - Claude DELANNOY - EYROLLES
- Programmation Windows en Turbo C++ et Borland C++ - Gérard LEBLANC -
EYROLLES
- Programmer Windows avec Turbo C++ - Claude DELANNOY - EYROLLES
CHAPITRE 1 INCOMPATIBILITES ENTRE C ET C++
NOUVELLES POSSIBILITES DU C++
Dans ce premier chapitre sont exposés les «plus» et les différences du C++ par rapport au C, sans toutefois aborder la programmation orientée objet (P.O.O.) qui sera vue lors d’un prochain chapitre.
I- COMMENTAIRES
En C++, les commentaires s’écrivent derrière un // et se terminent avec la fin de la ligne.
Exemple: void main() // ceci est un commentaire
{ // noter que le qualificatif «void» est obligatoire int n=2; // devant «main()»
}
II- LE QUALIFICATIF « CONST »
En C++, de telles déclarations de tableaux sont autorisées:
const int taille = 5;
char tab1[taille];
float tab2[taille + 1][taille *2];
III- DECLARATION DES VARIABLES
En C++, on peut déclarer les variables LOCALES au moment où on en a besoin. Si une variable locale est déclarée au début d’un bloc, sa portée est limitée à ce bloc.
Exemple: void main()
{
for ( int i = 0;i<10;i++ ) // la variable i est connue de toute la
fonction { // «main()»
int j; // la variable j n’est connue que du bloc
...;
...;
}
}
Ce exemple est équivalent à:
void main()
{
int i;
for ( i = 0;i<10;i++ ) // la variable i est connue de toute la fonction
{ // « main() »
int j; // la variable j n’est connue que du bloc
...;
...;
}
}
Cette possibilité autorise une gestion plus précise de la mémoire, mais peut nuire à la structuration des programmes.
IV- LES NOUVELLES POSSIBILITES D’ENTREES/SORTIES
On peut utiliser en C++ les fonctions d’entrées/sorties classiques du C (printf, scanf, puts, gets, putc, getc ...), à condition de déclarer le fichier d’en-tête stdio.h.
Il existe de nouvelles possibilités en C++, à condition de déclarer le fichier d’en-tête iostream.h.
Ces nouvelles possibilités ne nécessitent pas de FORMATAGE des données.
Sortie sur écran: l’opérateur cout:
Exemples: cout << "BONJOUR"; // équivalent à puts("BONJOUR");
int n = 25;
cout << "Valeur: "; // équivalent à puts("Valeur");
cout << n; // équivalent à printf("%d",n);
On peut encore écrire directement:
cout <<"Valeur:" << n;
cout <<"\n "; | // pour aller à la ligne |
char c = ‘a’; | |
cout << c; | // affiche le caractère, il faut utiliser |
// printf (formatage), pour obtenir le code |
ASCII
Cette notation sera justifiée lors du chapitre sur les flots.
L’opérateur cout permet d’afficher des nombres entiers ou réels, des caractères, des chaînes de caractères, des pointeurs autre que de type *char (valeur de l’adresse).
Exemple (à tester) et exercice I-1:
#include <iostream.h> #include <conio.h> void main()
{ int i,n=25,*p; char *tc="On essaie cout !"; float x = 123.456;
cout<<"BONJOUR\n"; cout<<tc<<"\n"; cout<<"BONJOUR\n"<<tc<<"\n"; cout<<"n= "<<n<<" x= "<<x<<" p= "<<p<<"\n"; getch() ;
}
Saisie clavier: l’opérateur cin:
Exemples: int n;
cout<<"Saisir un entier: ";
cin >> n; // équivalent à scanf("%d",&n);
int a, b;
cin >> a >> b; // saisie de 2 entiers séparés par un Retour Charriot
Cette notation sera justifiée lors du chapitre sur les flots.
L’opérateur cin permet de saisir des nombres entiers ou réels, des caractères, des chaînes de caractères.
Exemple (à tester) et exercice I-2:
Tester cet exemple plusieurs fois, notamment en effectuant des saisies erronées de sorte d’évaluer les « anomalies » de fonctionnement de cin.
#include <iostream.h> #include <conio.h>
void main()
{ int n; char tc[30],c; float x;
cout<<"Saisir un entier:"; cin>>n;
cout<<"Saisir un réel:"; cin>>x;
cout<<"Saisir une phrase:"; cin>>tc;
cout<<"Saisir une lettre:"; cin>>c;
cout<<"Relecture: "<<n<<" "<<x<<" "<<tc<<" "<<c<<"\n"; getch() ; }
V- LES CONVERSIONS DE TYPE
Le langage C++ autorise les conversions de type entre variables de type char, int, float, double:
Exemple:
void main()
{
char c=0x56,d=25,e; int i=0x1234,j; float r=678.9,s;
j = c; | // j vaut 0x0056 |
j = r; | // j vaut 678 |
s = d; | // s vaut 25.0 |
e = i; } | // e vaut 0x34 |
Une conversion de type float --> int ou char est dite dégradante
Une conversion de type int ou char --> float est dite nondégradante
VI- LES CONVERSIONS DE TYPE LORS D’APPEL A FONCTION
Le C++, contrairement au C, autorise, dans une certaine mesure, le non-respect du type des arguments lors d’un appel à fonction: le compilateur opère alors une conversion de type.
Exemple: double ma_fonction(int u, float f)
{
.....; // fonction avec passage de deux paramètres
.....;
}
void main()
{
char c; int i, j; float r; double r1, r2, r3, r4;
r1 = ma_fonction( i, r ); | // appel standard | |
r2 = ma_fonction( c, r); | // appel correct, c est converti en int | |
r3 = ma_fonction( i, j); | // appel correct, j est converti en float | |
r4 = ma_fonction( r, j); | // appel correct, r est converti en int | |
// et j est converti en float | ||
Exercice I-3: | } |
Ecrire une fonction float puissance(float x,int n) qui renvoie xn. La mettre en oeuvre en utilisant les propriétés de conversion de type.
VII- LES ARGUMENTS PAR DEFAUT
En C++, on peut préciser la valeur prise par défaut par un argument de fonction. Lors de l’appel à cette fonction, si on omet l’argument, il prendra la valeur indiquée par défaut, dans le cas contraire, cette valeur par défaut est ignorée.
Exemple: void f1(int n = 3) // par défaut le paramètre n vaut 3
{
....;
}
void f2(int n, float x = 2.35) // par défaut le paramètre x vaut 2.35
{
....;
}
void f3(char c, int n = 3, float x = 2.35) // par défaut le paramètre n vaut 3
{ // et le paramètre x vaut
2.35
....;
}
void main()
{
char a = 0; int i = 2; float r = 5.6;
f1(i); // l’argument n vaut 2, l’initialisation par défaut est ignorée
f1(); // l’argument n prend la valeur par défaut f2(i,r); // les initialisations par défaut sont ignorées f2(i); // le second paramètre prend la valeur par défaut
// f2(); interdit
f3(a, i, r); // les initialisations par défaut sont ignorées f3(a, i); // le troisième paramètre prend la valeur par défaut
f3(a); // le deuxième et la troisième paramètres prennent les
valeurs } // par défaut
Remarque:
Les arguments, dont la valeur est fournie par défaut, doivent OBLIGATOIREMENT se situer en fin de liste.
La déclaration suivante est interdite: void f4(char c=2, int n)
{
....;
}
Exercice I-4:
Reprendre l’exercice précédent. Par défaut, la fonction puissance devra fournir x4.
VIII- LA SURDEFINITION DES FONCTIONS
Le C++ autorise la définition de fonctions différentes et portant le même nom. Dans ce cas, il faut les différencier par le type des arguments.
Exemple (à tester) et exercice I-5:
#include <iostream.h> #include <conio.h>
void test(int n = 0,float x = 2.5)
{
cout <<"Fonction N°1 : "; cout << "n= "<<n<<" x="<<x<<"\n";
}
void test(float x = 4.1,int n = 2)
{
cout <<"Fonction N°2 : "; cout << "n= "<<n<<" x="<<x<<"\n";
}
void main()
{
int i = 5; float r = 3.2;
test(i,r); // fonction N°1 test(r,i); // fonction N°2 test(i); // fonction N°1
test(r); // fonction N°2
// les appels suivants, ambigüs, sont rejetés par le compilateur
// test();
// test (i,i);
// test (r,r);
// les inialisations par défaut de x à la valeur 4.1
// et de n à 0 sont inutilisables getch() ;}
Exemple (à tester) et exercice I-6:
#include <iostream.h> #include <conio.h> void essai(float x,char c,int n=0)
{cout<<"Fonction N°1: x = "<<x<<" c = "<<c<<" n = "<<n<<"\n";}
void essai(float x,int n)
{cout<<"Fonction N°2: x = "<<x<<" n = "<<n<<"\n";}
void main()
{
char l='z';int u=4;float y = 2.0; essai(y,l,u); // fonction N°1 essai(y,l); // fonction N°1 essai(y,u); // fonction N°2 essai(u,u); // fonction N°2 essai(u,l); // fonction N°1 // essai(y,y); rejet par le complateur essai(y,y,u); // fonction N°1 getch() ;}
Exercice I-7:
Ecrire une fonction void affiche (float x, int n = 0) qui affiche xn (avec en particulier x0 = 1 et donc, 00 = 1).
Ecrire une autre fonction void affiche(int n, float x=0) qui affiche xn (avec en particulier 0n = 0 et donc, 00 = 0).
Les mettre en oeuvre dans le programme principal, en utilisant la propriété de surdéfinition.
Remarque: Cet exemple conduit à une erreur de compilation lors d’appel de type mn avec m et n entiers.
IX- LES OPERATEURS new ET delete
Ces deux opérateurs remplacent malloc et free (que l’on peut toujours utiliser). Ils permettent donc de réserver de la place en mémoire, et d’en libérer.
Exemples: int *ad; // déclaration d’un pointeur sur un entier ad = new int; // réservation de place en mémoire pour un entier
On aurait pu déclarer directement int *ad = new int;
char *adc;
adc = new char[100]; // réservation de place en mémoire pour 100 caractères
On aurait pu déclarer directement char *adc = new char[100];
int *adi;
adi = new int[40]; // réservation de place en mémoire pour 40 entiers
On aurait pu déclarer directement int *adi = new int[40];
delete ad; // libération de place
delete adc; delete adi; Exemple (à tester) et exercice I-8:
#include <iostream.h> // new et delete
#include <conio.h> // verifier en testant que cin et cout posent les
// memes pb que scanf et printf (flux d'E-S) void main()
{
int *ad = new int; char *adc;
adc = new char[25];
cout<<"Entrer un nombre:"; cin>>*ad;
cout<<"Voici ce nombre:"<<*ad;
cout<<"\nEntrer une phrase:"; cin>>adc;
cout<<"Voici cette phrase:"<<adc;
delete ad; delete adc;
getch() ;}
Exercice I-9:
Déclarer un tableau de 5 réels. Calculer et afficher leur moyenne.
Remarques:
- Il ne faut pas utiliser conjointement malloc et delete ou bien new et free.
- En TURBO C++, l’opérateur new permet de réserver au maximum 64 Koctets en mémoire; la fonction set_new_handler permet de gérer cette limite.
X- NOTION DE REFERENCE
En C, la notation &n signifie « l’adresse de la variable n » En C++, cette notation possède deux significations:
- Il peut toujours s’agir de l’adresse de la variable n
- Il peut aussi s’agir de la référence à n
Seul le contexte du programme permet de déterminer s’il s’agit de l’une ou l’autre des deux significations.
Exemple: int n;
int &p = n; // p est une référence à n
// p occupe le même emplacement mémoire que n
n = 3;
cout<< p; // l’affichage donnera 3
XI -PASSAGE DE PARAMETRE PAR REFERENCE
Rappel:
En C, comme en C++, un sous-programme ne peut modifier la valeur d’une variable locale passée en argument de fonction. Pour se faire, en C, il faut passer l’adresse de la variable.
Exemple (à tester) et exercices I-10 et I-11:
#include <iostream.h> // passage par valeur
#include <conio.h>
void echange(int a,int b)
{
int tampon;
tampon = b; b = a; a = tampon;
cout<<"Pendant l'échange: a = "<<a<<" b = "<<b<<"\n";
}
void main()
{
int u=5,v=3;
cout<<"Avant echange: u = "<<u<<" v = "<<v<<"\n"; echange(u,v);
cout<<"Apres echange: u = "<<u<<" v = "<<v<<"\n"; getch() ;}
L’échange n’a pas lieu.
#include <iostream.h> // passage par adresse
#include <conio.h>
void echange(int *a,int *b)
{
int tampon;
tampon = *b; *b = *a; *a = tampon;
cout<<"Pendant l'échange: a = "<<*a<<" b = "<<*b<<"\n";
}
void main()
{
int u=5,v=3;
cout<<"Avant echange: u = "<<u<<" v = "<<v<<"\n"; echange(&u,&v);
cout<<"Apres echange: u = "<<u<<" v = "<<v<<"\n"; getch() ;}
L’échange a lieu.
En C++, on préférera le passage par référence:
Exemple (à tester) et exercice I-12:
#include <iostream.h> //passage par référence
#include <conio.h>
void echange(int &a,int &b) // référence à a et b
{
int tampon;
tampon = b; b = a; a = tampon;
cout<<"Pendant l'échange: a = "<<a<<" b = "<<b<<"\n";
}
void main()
{
int u=5,v=3;
cout<<"Avant echange: u = "<<u<<" v = "<<v<<"\n"; echange(u,v);
cout<<"Apres echange: u = "<<u<<" v = "<<v<<"\n"; getch() ;}
L’échange a lieu. Le compilateur prend en charge le passage par adresse si celui-ci est nécessaire.
Remarquer la simplification de l’écriture de la fonction.
XII- CORRIGE DES EXERCICES
Exercice I-3:
#include <iostream.h> #include <conio.h> float puissance(float x,int n)
{
float resultat=1;
for(int i=1;i<=n;i++)resultat = resultat * x; return resultat;
}
void main()
{
char c=5;int i=10,j=6; float r=2.456,r1,r2,r3,r4,r5; r1 = puissance(r,j); r2 = puissance(r,c); r3 = puissance(j,i); r4 = puissance(j,r); r5 = puissance(0,4); cout << "r1 = " <<r1<<"\n"; cout << "r2 = " <<r2<<"\n"; cout << "r3 = " <<r3<<"\n"; cout << "r4 = " <<r4<<"\n"; cout << "r5 = " <<r5<<"\n";
getch() ;}
Exercice I-4:
#include <iostream.h> #include <conio.h> float puissance(float x,int n=4)
{
float resultat=1;
for(int i=1;i<=n;i++)resultat = resultat * x; return resultat;
}
void main()
{
int j=6; float r=2.456,r1,r2,r3,r4,r5; r1 = puissance(r,j); r2 = puissance(r); r3 = puissance(1.4,j); r4 = puissance(1.4); cout << "r1 = " <<r1<<"\n"; cout << "r2 = " <<r2<<"\n"; cout << "r3 = " <<r3<<"\n"; cout << "r4 = " <<r4<<"\n";
getch() ;}
Exercice I-7:
#include <iostream.h> #include <conio.h> void affiche(float x,int n=0) {int i = 1;float resultat = 1; for(;i<=n;i++)resultat = resultat * x;
cout << "x = "<<x<< " n = " << n << " resultat = " << resultat <<"\n";}
void affiche(int n,float x=0) {int i = 1;float resultat = 1;
if (n!=0){for(;i<=n;i++)resultat = resultat * x;} else (resultat = 0);
cout << "n = "<<n<< " x = " << x << " resultat = " << resultat <<"\n";
}
void main()
{
int u=4,v=0;float y = 2.0,z=0; affiche(u); affiche(y); affiche(y,u); affiche(u,y); affiche(v,z); affiche(z,v); getch() ;}
Exercice I-9:
#include <iostream.h> #include <conio.h>
void main()
{
float moyenne =0,*tab = new float[5]; int i=0;
for(;i<5;i++)
{
cout<<"Entrer un nombre: "; cin>>tab[i];
moyenne = moyenne + tab[i];
}
moyenne = moyenne/5;
cout <<"Moyenne = "<<moyenne<<"\n";
delete tab;
getch() ;}
CHAPITRE 2
PROGRAMMATION ORIENTE OBJET: NOTION DE CLASSE
I- INTRODUCTION
On attend d’un programme informatique
- l’exactitude (réponse aux spécifications)
- la robustesse (réaction correcte à une utilisation « hors normes »)
- l’extensibilité (aptitude à l’évolution)
- la réutilisabilité (utilisation de modules)
- la portabilité (support d’une autre implémentation)
- l’efficience (performance en termes de vitesse d’exécution et de consommation mémoire)
Les langages évolués de type C ou PASCAL, reposent sur le principe de la programmation structurée (algorithmes + structures de données)
Le C++ et un langage orienté objet. Un langage orienté objet permet la manipulation de classes. Comme on le verra dans ce chapitre, la classe généralise la notion de structure.
Une classe contient des variables (ou « données ») et des fonctions (ou « méthodes ») permettant de manipuler ces variables.
Les langages « orientés objet » ont été développés pour faciliter l’écriture et améliorer la qualité des logiciels en termes de modularité.
Un langage orienté objet sera livré avec une bibliothèque de classes. Le développeur utilise ces classes pour mettre au point ses logiciels.
Rappel sur la notion de prototype de fonction:
En C++, comme en C, on a fréquemment besoin de déclarer des prototypes de fonctions.
Par exemple, dans les fichiers d’en-tête (de type *.h), sont déclarés les prototypes des fonctions appelées par le programme.
Le prototype d’une fonction est constitué du nom de la fonction, du type de la valeur de retour, du type des arguments à passer
Exemples: void ma_fonction1()
void ma_fonction2(int n, float u) | // prototype «complet» |
void ma_fonction2(int, float) | // prototype «réduit» |
int ma_fonction3(char *x) | // prototype «complet» |
int ma_fonction3(char *) | // prototype «réduit» |
int ma_fonction4(int &u) | // prototype «complet» |
int ma_fonction4(int &) | // prototype «réduit» |
On utilise indifféremment, dans les fichiers d’en-tête, le prototype complet ou le prototype réduit.
II- NOTION DE CLASSE
Exemple (à tester) et exercice II-1 :
(il faut ajuster la temporisation aux performances de la machine utilisée)
#include <iostream.h> // les classes
#include <conio.h>
class point
{ int x,y;
public: void initialise(int,int); void deplace(int,int);
void affiche();
};
void point::initialise(int abs,int ord)
{x = abs; y = ord;}
void point::deplace(int dx,int dy)
{x = x+dx; y = y+dy;}
void point::affiche()
{gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void tempo(int duree)
{float stop ;stop = duree*10000.0;for(;stop>0;stop=stop-1.0);}
void main()
{ point a,b;
a.initialise(1,4);
a.affiche(); tempo(10);
a.deplace(17,10);
a.affiche();
b = a; // affectation autorisee tempo(15); clrscr();
b.affiche(); getch() ;}
«point» est uneclasse. Cette classe est constituée des donnéesx et y et des fonctionsmembres (ou méthodes) « initialise », « deplace », « affiche ». On déclare la classe en début de programme (données et prototype des fonctions membres), puis on définit le contenu des fonctions membres.
Les données x et y sont dites privées. Ceci signifie que l’on ne peut les manipuler qu’au travers des fonctions membres. On dit que le langage C++ réalise l’encapsulation des données.
a et b sont des objets de classe «point», c’est-à-dire des variables de type «point». On a défini ici un nouveau type de variable, propre à cet application, comme on le fait en C avec les structures.
Suivant le principe dit de « l’encapsulation des données », la notation a.x est interdite.
Exercice II-2:
Utiliser la classe « point » précédente. Ecrire une fonction de prototype void test() dans laquelle on déclare un point u, on l’initialise, on l’affiche , on le déplace et on l’affiche à nouveau. Le programme principal main ne contient que l’appel à test.
Exercice II-3:
Ecrire une fonction de prototype void test(point &u) (référence) similaire. Ne pas déclarer de point local dans test. Déclarer un point local a dans le programme principal main et appeler la fonction test en passant le paramètre a.
Exercice II-4:
Ecrire une fonction de prototype point test() qui retourne un point. Ce point sera initialisé et affiché dans test puis déplacé et à nouveau affiché dans main.
III- NOTION DE CONSTRUCTEUR
Un constructeur est une fonction membre systématiquement exécutée lors de la déclaration d’un objet statique, automatique, ou dynamique.
On ne traitera dans ce qui suit que des objets automatiques.
Dans l’exemple de la classe point, le constructeur remplace la fonction membre initialise.
Exemple (à tester) et exercice II-5:
#include <iostream.h> // constructeur
#include <conio.h>
class point
{ int x,y;
public: point(); // noter le type du constructeur (pas de "void")
void deplace(int,int);
void affiche();
};
point::point() // initialisation par default {x = 20; y = 10;} // grace au constructeur void point::deplace(int dx,int dy)
{x = x+dx; y = y+dy;}
void point::affiche()
{gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void tempo(int duree)
{float stop ;stop = duree*10000.0;for(;stop>0;stop=stop-1.0);}
void main()
{
point a,b; // les deux points sont initialisés en 20,10 a.affiche(); tempo(10);
a.deplace(17,10);
a.affiche();
tempo(15); clrscr();
b.affiche();
getch() ;}
Exemple (à tester) et exercice II-6:
#include <iostream.h> // constructeur
#include <conio.h>
class point
{ int x,y;
public: point(int,int); // noter le type du constructeur (pas de "void")
void deplace(int,int); void affiche();
};
point::point(int abs,int ord) // initialisation par default
{x = abs; y = ord;} // grace au constructeur, ici paramètres à passer
void point::deplace(int dx,int dy)
{x = x+dx; y = y+dy;}
void point::affiche()
{gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void tempo(int duree)
{float stop ;stop = duree*10000.0;for(;stop>0;stop=stop-1.0);}
void main()
{
point a(20,10),b(30,20); // les deux points sont initialises:a en 20,10 b en 30,20 a.affiche(); tempo(10);
a.deplace(17,10);
a.affiche(); tempo(15); clrscr();
b.affiche(); getch() ;}
Exercice II-7: Reprendre l’exercice II-2, en utilisant la classe de l’exercice II-6
Exercice II-8: Reprendre l’exercice II-3, en utilisant la classe de l’exercice II-6
Exercice II-9: Reprendre l’exercice II-4, en utilisant la classe de l’exercice II-6
IV- NOTION DE DESTRUCTEUR
Le destructeur est une fonction membre systématiquement exécutée «à la fin de la vie » d’un objet statique, automatique, ou dynamique. On ne peut pas passer de paramètres par le destructeur.
On ne traitera dans ce qui suit que des objets automatiques.
Exemple (à tester) et exercice II-10:
#include <iostream.h> // destructeur
#include <conio.h>
class point
{ int x,y; public: point(int,int);
void deplace(int,int);
void affiche();
~point(); // noter le type du destructeur
};
point::point(int abs,int ord) // initialisation par default
{x = abs; y = ord;} // grace au constructeur, ici paramètres à passer
void point::deplace(int dx,int dy)
{x = x+dx; y = y+dy;}
void point::affiche()
{gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
point::~point()
{cout<<"Frapper une touche...";getch();
cout<<"destruction du point x ="<<x<<" y="<<y<<"\n";}
void tempo(int duree)
{float stop ;stop = duree*10000.0;for(;stop>0;stop=stop-1.0);}
void test()
{ point u(3,7); u.affiche(); tempo(20);
}
void main()
{point a(1,4);a.affiche();tempo(20); test();
point b(5,10);b.affiche(); getch() ;}
V- ALLOCATION DYNAMIQUE
Lorsque les membres données d’une classe sont des pointeurs, le constructeur est utilisé pour l’allocation dynamique de mémoire sur ce pointeur. Le destructeur est utilisé pour libérer la place.
Exemple (à tester) et exercice II-11:
#include <iostream.h> // Allocation dynamique de données membres
#include <stdlib.h>
#include <conio.h>
class calcul {int nbval,*val;
public: calcul(int,int); // constructeur ~calcul(); // destructeur
void affiche();
};
calcul::calcul(int nb,int mul) //constructeur
{int i;
nbval = nb; val = new int[nbval]; // reserve de la place
for(i=0;i<nbval;i++)val[i] = i*mul;
}
calcul::~calcul()
{delete val;} // abandon de la place reservee
void calcul::affiche()
{int i;
for(i=0;i<nbval;i++)cout<<val[i]<<" "; cout<<"\n";
}
void main()
{ clrscr();
calcul suite1(10,4); suite1.affiche(); calcul suite2(6,8); suite2.affiche();
getch() ;}
VI- EXERCICES RECAPITULATIFS
Exemple (à tester) et exercice II-12:
Cet exemple ne fonctionne qu’en environnement DOS. Il utilise les fonctions graphiques classiques du TURBO C. On crée une classe « losange », les fonctions membres permettent de manipuler ce losange.
#include <graphics.h>
#include <conio.h>
#include <iostream.h>
#include <dos.h>
class losange
{
int x,y,dx,dy,couleur; public: losange(); void deplace(int,int,int); void affiche();
void efface();
};
losange::losange() // constructeur
{x=100;y=100;dx=60;dy=100;couleur=BLUE;}
void losange::deplace(int depx,int depy,int coul)
{x=x+depx;y=y+depy;couleur=coul;}
void losange::affiche() {int tab[10];
tab[0]=x;tab[1]=y;tab[2]=x+dx/2;tab[3]=y+dy/2; tab[4]=x;tab[5]=y+dy;tab[6]=x-dx/2;tab[7]=y+dy/2; tab[8]=x;tab[9]=y;
setfillstyle(SOLID_FILL,couleur);
fillpoly(5,tab);
}
void losange::efface() {int tab[10];
tab[0]=x;tab[1]=y;tab[2]=x+dx/2;tab[3]=y+dy/2; tab[4]=x;tab[5]=y+dy;tab[6]=x-dx/2;tab[7]=y+dy/2; tab[8]=x;tab[9]=y;
setcolor(getbkcolor()); // pour effacer le contour setfillstyle(SOLID_FILL,getbkcolor());
fillpoly(5,tab);
}
void init_graph()
{
int gd,gm; detectgraph(&gd,&gm); initgraph(&gd,&gm,"c:\\cplus\\bgi");
setbkcolor(YELLOW);
}
void main()
{ losange l; init_graph(); l.affiche(); getch();closegraph();
}
Exercice II- 13: Modifier le programme principal de sorte de faire clignoter le losange tant que l’utilisateur n’a pas appuyé sur une touche.
Exercice II- 14: Modifier le programme principal de sorte de déplacer le losange d’une position à une autre position, tant que l’utilisateur n’a pas appuyé sur une touche.
VII- CORRIGE DES EXERCICES
Exercice II-2:
#include <iostream.h> // les classes
#include <conio.h>
class point
{ int x,y; public: void initialise(int,int); void deplace(int,int);
void affiche();
};
void point::initialise(int abs,int ord)
{x = abs; y = ord;}
void point::deplace(int dx,int dy)
{x = x+dx; y = y+dy;}
void point::affiche()
{gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void tempo(int duree)
{float stop ;stop = duree*10000.0;for(;stop>0;stop=stop-1.0);}
void test()
{
point u;
u.initialise(1,4);u.affiche(); tempo(10);
u.deplace(17,10);u.affiche();
}
void main()
{test();getch() ;}
Exercice II-3:
#include <iostream.h> // les classes
#include <conio.h>
class point
{ int x,y;
public: void initialise(int,int); void deplace(int,int);
void affiche();
};
void point::initialise(int abs,int ord)
{x = abs; y = ord;}
void point::deplace(int dx,int dy)
{x = x+dx; y = y+dy;}
void point::affiche()
{gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void tempo(int duree)
{float stop ;stop = duree*10000.0;for(;stop>0;stop=stop-1.0);}
void test(point &u)
{
u.initialise(1,4);u.affiche(); tempo(10);
u.deplace(17,10);u.affiche();
}
void main()
{point a;test(a);getch() ;}
Exercice II-4:
#include <iostream.h> // les classes
#include <conio.h>
class point
{ int x,y; public: void initialise(int,int); void deplace(int,int);
void affiche();
};
void point::initialise(int abs,int ord)
{x = abs; y = ord;}
void point::deplace(int dx,int dy)
{x = x+dx; y = y+dy;}
void point::affiche()
{gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void tempo(int duree)
{float stop ;stop = duree*10000.0;for(;stop>0;stop=stop-1.0);}
point test()
{point u;
u.initialise(1,4);u.affiche();return u;}
void main() {point a; a = test(); tempo(10);
a.deplace(17,10);a.affiche();getch() ;}
Exercice II-7:
#include <iostream.h>
#include <conio.h>
class point
{ int x,y;
public: point(int,int); // noter le type du constructeur (pas de "void")
void deplace(int,int); void affiche();
};
point::point(int abs,int ord) // initialisation par default
{x = abs; y = ord;} // grace au constructeur, ici paramètres à passer
void point::deplace(int dx,int dy)
{x = x+dx; y = y+dy;}
void point::affiche()
{gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void tempo(int duree)
{float stop ;stop = duree*10000.0;for(;stop>0;stop=stop-1.0);}
void test()
{ point u(1,4); u.affiche(); tempo(10);
u.deplace(17,10);
u.affiche();
}
void main()
{test();getch() ;}
Exercice II-8:
#include <iostream.h> // les classes
#include <conio.h>
class point
{ int x,y;
public: point(int,int); // noter le type du constructeur (pas de "void")
void deplace(int,int); void affiche();
};
point::point(int abs,int ord) // initialisation par default
{x = abs; y = ord;} // grace au constructeur, ici paramètres à passer
void point::deplace(int dx,int dy)
{x = x+dx; y = y+dy;}
void point::affiche()
{gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void tempo(int duree)
{float stop ;stop = duree*10000.0;for(;stop>0;stop=stop-1.0);}
void test(point &u)
{
u.affiche(); tempo(10);
u.deplace(17,10);u.affiche();
}
void main()
{point a(1,4);test(a);getch() ;}
Exercice II-9:
#include <iostream.h> // les classes
#include <conio.h>
class point
{ int x,y;
public: point(int,int); // noter le type du constructeur (pas de "void")
void deplace(int,int);
void affiche();
};
point::point(int abs,int ord) // initialisation par default
{x = abs; y = ord;} // grace au constructeur, ici paramètres à passer
void point::deplace(int dx,int dy)
{x = x+dx; y = y+dy;}
void point::affiche()
{gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void tempo(int duree)
{float stop ;stop = duree*10000.0;for(;stop>0;stop=stop-1.0);}
point test()
{point u(5,6);
u.affiche();return u;}
void main() {point a(1,4); a.affiche();
tempo(15); a = test(); tempo(10);
a.deplace(17,10);a.affiche();}
Exercice II-13:
void main()
{ losange l; init_graph();
while(!kbhit())
{
l.affiche(); delay(500);
l.efface(); delay(500); }
getch();closegraph();
}
Exercice II-14:
void main()
{ losange l; init_graph(); while(!kbhit())
{
l.affiche(); delay(500);l.efface();
l.deplace(150,150,RED);
l.affiche();delay(500);l.efface();
l.deplace(-150,-150,BLUE);
}
getch();closegraph();
}
CHAPITRE 3
PROPRIETES DES FONCTIONS MEMBRES
I- SURDEFINITION DES FONCTIONS MEMBRES
En utilisant la propriété de surdéfinition des fonctions du C++, on peut définir plusieurs constructeurs, ou bien plusieurs fonctions membres, différentes, mais portant le même nom.
Exemple (à tester) et exercice III-1: Définition de plusieurs constructeurs:
#include <iostream.h> // Surdefinition de fonctions
#include <conio.h>
class point
{ int x,y;
public: point(); // constructeur 1 point(int);// constructeur 2 point(int,int);// constructeur 3 void affiche();
};
point::point() // constructeur 1
{x=0;y=0;}
point::point(int abs) // constructeur 2
{x = abs; y = abs;}
point::point(int abs,int ord) // constructeur 3
{x = abs; y = ord;}
void point::affiche()
{gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void main()
{ clrscr(); point a,b(5); a.affiche();
b.affiche(); point c(3,12); c.affiche();
getch() ;}
Exercice III-2: Surdéfinition d’une fonction membre
Ecrire une deuxième fonction affiche de prototype void point::affiche(char*message)
Cette fonction donne la possibilité à l’utilisateur d’ajouter, à la position du point, un message sur l’écran.
II- FONCTIONS MEMBRES « EN LIGNE »
Le langage C++ autorise la description des fonctions membres dès leur déclaration dans la classe. On dit que l’on écrit une fonction « inline ».
Il s’agit alors d’une « macrofonction »: A chaque appel, il y a génération du code de la fonction et non appel à un sous-programme.
Les appels sont donc plus rapides mais cette méthode génère davantage de code.
Exemple (à tester) et exercice III-3:
Comparer la taille des fichiers et
#include <iostream.h> // Surdefinition de fonctions
#include <conio.h>
class point
{ int x,y;
public: point(){x=0;y=0;} // constructeur 1 point(int abs){x=abs;y=abs;}// constructeur 2 point(int abs,int ord){x=abs;y=ord;}// constructeur 3
void affiche();
};
void point::affiche()
{gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void main()
{ point a,b(5); a.affiche();
b.affiche(); point c(3,12); c.affiche(); getch() ;
}
III- INITIALISATION DES PARAMETRES PAR DEFAUT
Exemple (à tester) et exercice III-4:
#include <iostream.h> // Fonctions membres « en ligne »
#include <conio.h>
class point
{ int x,y;
public: point(int abs=0,int ord=2){x=abs;y=ord;}// constructeur void affiche(char* = "Position du point"); // argument par defaut
};
void point::affiche(char *message) {gotoxy(x,y-1);cout<<message;
gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void main()
{
point a,b(40); a.affiche();
b.affiche("Point b"); char texte[10]="Bonjour"; point c(3,12);
c.affiche(texte); getch() ;
}
IV- OBJETS TRANSMIS EN ARGUMENT D’UNE FONCTION MEMBRE
Quand on passe comme paramètre à une fonction membre ... un objet de la classe à laquelle appartient cette fonction:
1- Passage par valeur
Exemple (à tester) et exercice III-5:
#include <iostream.h>
#include <conio.h> // objets transmis en argument d'une fonction membre
class point
{ int x,y;
public: point(int abs = 0,int ord = 2){x=abs;y=ord;}// constructeur
int coincide(point);
};
int point::coincide(point pt)
{if ((pt.x == x) && (pt.y == y)) return(1);else return(0);}
// noter la dissymetrie des notations pt.x et x
void main()
{ int test1,test2; point a,b(1),c(0,2); test1 = a.coincide(b); test2 = b.coincide(a);
cout<<"a et b:"<<test1<<" ou "<<test2<<"\n"; test1 = a.coincide(c); test2 = c.coincide(a);
cout<<"a et c:"<<test1<<" ou "<<test2<<"\n"; getch() ;
}
Noter que l’on rencontre la notation « pt.x » ou « pt.y » pour la première fois. Elle n’est autorisée qu’à l’intérieur d’une fonction membre (x et y membres privés de la classe).
On verra plus tard que le passage d’un objet par valeur pose problème si certains membres de la classe sont des pointeurs. Il faudra alors prévoir une allocation dynamique de mémoire via un constructeur.
2- Passage par adresse
Exercice III-6: Modifier la fonction membre coincide de l’exercice prcédent de sorte que son prototype devienne int point::coincide(point *adpt). Ré-écrire le programme principal en conséquence.
3- Passage par référence
Exercice III-7: Modifier à nouveau la fonction membre coincide de sorte que son prototype devienne int point::coincide(point &pt). Ré-écrire le programme principal en conséquence.
V- EXERCICES RECAPITULATIFS
On définit la classe vecteur comme ci-dessous:
class vecteur {float x,y;
public: vecteur(float,float);
void homotethie(float);
void affiche();
};
vecteur::vecteur(float abs =0.,float ord = 0.)
{x=abs;y=ord;}
void vecteur::homotethie(float val)
{x = x*val; y = y*val;}
void vecteur::affiche()
{cout<<"x = "<<x<<" y = "<<y<<"\n";}
Exercice III-8: La mettre en oeuvre dans void main(), en ajoutant une fonction membre float det(vecteur) qui retourne le déterminant des deux vecteurs (celui passé en paramètre et celui de l’objet).
Exercice III-9: Modifier la fonction déterminant de sorte de passer le paramètre par adresse.
Exercice III-10: Modifier la fonction déterminant de sorte de passer le paramètre par référence.
VI- OBJET RETOURNE PAR UNE FONCTION MEMBRE
Que se passe-t-il lorsqu’une fonction membre retourne elle-même un objet ?
1- Retour par valeur
Exemple (à tester) et exercice III-11: (la fonction concernée est la fonction symetrique)
#include <iostream.h>
#include <conio.h>
// La valeur de retour d'une fonction est un objet
// Transmission par valeur
class point
{ int x,y;
public: point(int abs = 0,int ord = 0){x=abs;y=ord;}// constructeur point symetrique(); void affiche();
};
point point::symetrique()
{point res; res.x = -x; res.y = -y; return res;
}
void point::affiche()
{cout<<"Je suis en "<<x<<" "<<" "<<y<<"\n";}
void main() {point a,b(1,6);
a=b.symetrique();a.affiche();b.affiche(); getch() ;}
2- Retour par adresse (***)
Exemple (à tester) et exercice III-12:
#include <iostream.h>
#include <conio.h>
// La valeur de retour d'une fonction est un objet
// Transmission par adresse
class point
{ int x,y;
public: point(int abs = 0,int ord = 0){x=abs;y=ord;}// constructeur point *symetrique(); void affiche();
};
point *point::symetrique()
{point *res; res = new point; res->x = -x; res->y = -y; return res;
}
void point::affiche()
{cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void main()
{ point a,b(1,6);
a = *b.symetrique();a.affiche();b.affiche(); getch() ;}
3- Retour par référence (***)
La valeur retournée l’est par référence. On en verra l’usage dans un prochain chapitre.
Exemple (à tester) et exercice III-13:
#include <iostream.h>
#include <conio.h>
// La valeur de retour d'une fonction est un objet
// Transmission par reference
class point
{ int x,y;
public: point(int abs = 0,int ord = 0){x=abs;y=ord;}// constructeur point &symetrique();
void affiche();
};
point &point::symetrique() // La variable res est obligatoirement static
{static point res; // Pour passer par reference res.x = -x; res.y = -y; return res;
}
void point::affiche()
{cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void main()
{ point a,b(1,6);
a=b.symetrique();a.affiche();b.affiche(); getch() ;}
Remarque: « res » et « b.symetrique »occupent le même emplacement mémoire (car « res » est une référence à « b.symetrique ». On déclare donc « res » comme variable static, sinon, cet objet n’existerait plus après être sorti de la fonction.
VII- EXERCICES RECAPITULATIFS
Exercice III-14: Reprendre la classe vecteur. Modifier la fonction homotéthie, qui retourne le vecteur modifié. (prototype: vecteur vecteur::homotethie(float val)).
Exercice III-15 (***): Même exercice, le retour se fait par adresse.
Exercice III-16 (***): Même exercice, le retour se fait par référence.
VIII- LE MOT CLE « THIS »
Ce mot désigne l’adresse de l’objet invoqué. Il est utilisable uniquement au sein d’une fonction membre.
Exemple (à tester) et exercice III-17:
#include <conio.h> // le mot cle THIS: pointeur sur l'objet l'ayant appel
#include <iostream.h> // utilisable uniquement dans une fonction membre
class point {int x,y; public: point(int abs=0,int ord=0) // constructeur en ligne
{x=abs;y=ord;} void affiche();
};
void point::affiche()
{cout<<"Adresse: "<<this<<" - Coordonnees: "<<x<<" "<<y<<"\n";}
void main() {point a(5),b(3,15);
a.affiche();b.affiche(); getch() ;}
Exercice III-18: Remplacer, dans l’exercice III-6, la fonction coincide par la fonction suivante:
int point::coincide(point *adpt)
{if ((this->x == adpt->x) && (this->y == adpt->y)) return(1);else return(0);}
IX- EXERCICE RECAPITULATIF
Exercice III-19: Reprendre la classe vecteur, munie du constructeur et de la fonction d’affichage. Ajouter
- Une fonction membre float vecteur::prod_scal(vecteur) qui retourne le produit scalaire des 2 vecteurs.
- Une fonction membre vecteur vecteur::somme(vecteur) qui retourne la somme des 2 vecteurs.
XI- CORRIGE DES EXERCICES
Exercice III-2:
#include <iostream.h> // Surdefinition de fonctions
#include <conio.h>
class point
{ int x,y; public: point(); // constructeur 1 point(int);// constructeur 2 point(int,int);// constructeur 3 void affiche();
void affiche(char *); // argument de type chaine
};
point::point() // constructeur 1
{x=0;y=0;}
point::point(int abs) // constructeur 2
{x=y=abs;}
point::point(int abs,int ord) // constructeur 3
{x = abs; y = ord;}
void point::affiche() // affiche 1
{gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void point::affiche(char *message) // affiche 2
{gotoxy(x,y-1);cout<<message;
gotoxy(x,y);cout<<"Je suis en "<<x<<" "<<y<<"\n";}
void main() {point a,b(5); a.affiche();
b.affiche("Point b:"); point c(3,12); char texte[10] = "Bonjour"; c.affiche(texte); getch() ;}
Exercice III-6:
#include <iostream.h>
#include <conio.h>
// objets transmis en argument d'une fonction membre - transmission de l'adresse
class point
{ int x,y;
public: point(int abs = 0,int ord = 2){x=abs;y=ord;}// constructeur int coincide(point *);
};
int point::coincide(point *adpt)
{if ((adpt->x == x) && (adpt->y == y)) return(1);else return(0);}
// noter la dissymetrie des notations pt->x et x
void main() {point a,b(1),c(0,2); int test1,test2; test1 = a.coincide(&b); test2 = b.coincide(&a);
cout<<"a et b:"<<test1<<" ou "<<test2<<"\n";
test1 = a.coincide(&c); test2 = c.coincide(&a);
cout<<"a et c:"<<test1<<" ou "<<test2<<"\n"; getch() ;}
Exercice III-7:
#include <iostream.h>
#include <conio.h>
// objets transmis en argument d'une fonction membre - transmission par reference
class point
{ int x,y;
public: point(int abs = 0,int ord = 2){x=abs;y=ord;}// constructeur
int coincide(point &);
};
int point::coincide(point &pt)
{if ((pt.x == x) && (pt.y == y)) return(1);else return(0);}
// noter la dissymetrie des notations pt.x et x
void main() {point a