![]() |
|
![]() |
L'intérêt du langage C++ réside dans l'utilisation de Classes construites par vous même ou par d'autres utilisateurs et qui peuvent être très utiles. Cette notion de classe est en faite la généralisation de la structure du C.
Une structure en C permet de définir un type (au sens du C) plus complexe que les types de bases. C'est un ensemble de variables structurées regroupées sous une même ``étiquette''. Considérons par exemple un point dans le plan ; celui-ci a 2 composantes X et Y. Il est alors pratique de pouvoir définir un type unique Point comportant deux champs ou attributs X et Y :
using namespace std;
struct Point
{
double X; // 1er champs
double Y; // 2eme champs
};
{
struct Point P1,P2; // on definit 2 variables P1 et P2 de types Point
P1.X=4.5; //on affecte le champs X de P1
P1.Y=2.5; //on affecte le champs Y de P1
P2.X=3.1; //on affecte le champ X de P2
P2.Y=5.1; //on affecte le champ Y de P2
cout«le point P1 a pour coordonnées : («P1.X«,«P1.Y«)«endl;
cout«le point P2 a pour coordonnées : («P2.X«,«P2.Y«)«endl;
}
![]() | |
![]() | |
![]() |
On peut bien sûr utiliser une classe ``comme une structure'' (c'est ce que nous allons commencer par faire) mais cela va beaucoup plus loin ; plutôt que de définir des types (au sens du C) elles permettent de définir des objets. Un objet c'est quelque choses qui possède des champs de variables (comme une structure) et des méthodes pour agir qui permettent de le manipuler (en particulier des méthodes pour le créer (ou construire) et le détruire).
La notion de classe est fondamentale en C++. On dit que le C++ est un langage orienté objets. Par rapport aux méthodes traditionnelles, la ``programmation objet'' est une nouvelle façon de programmer, car on va créer une classe (ou utiliser une classe existante) pour chaque ``concept'' qui semble ressortir du problème que l'on traite. Par exemple si l'on résout un problème d'algèbre linéaire, ce sera bien de programmer au préalable une classe de vecteurs et de matrices avec toutes leurs opérations standard. Le programme principal sera ainsi beaucoup plus simple en apparence, puisque on pourra écrire les opérations sous la forme W=M*N*V par exemple où V,W sont des vecteurs et M,N des matrices.
Un avantage de la programmation objet par rapport à la programmation habituelle, est donc que le code est mieux structuré et plus lisible. Un autre avantage est que le code est plus facilement réutilisable (Vous pouvez vous confectionner une bibliothèque de classes usuelles et/ou trouver des classes sur le web prêtes à l'emploi).
![]() |
Dans un premier temps, nous allons réécrire la structure Point sous la forme d'une classe.
using namespace std;
class Point //on declare la classe Point
{
public:
double X; // 1er attribut de Point
double Y; // 2eme attribut
}; //fin de la declaration de la classe. Noter le ;
int main()
{
Point P1,P2; // on definit 2 OBJETS Point P1 et P2
P1.X=4.5; //on affecte l'attribut X de P1
P1.Y=2.5; //on affecte l'attribut Y de P1
P2.X=3.1; //on affecte l'attribut X de P2
P2.Y=5.1; //on affecte l'attribut Y de P2
cout«le point P1 a pour coordonnées : («P1.X«,«P1.Y«)«endl;
cout«le point P2 a pour coordonnées : («P2.X«,«P2.Y«)«endl;
return 0;
}
![]() |
Dans une classe, on a des membres, qui sont soit des attributs (une variable) soit des méthodes ou fonctions membres (voir ci-après). Ces membres peuvent être private ou public1. Un membre public est directement accessible depuis l'extérieur de la classe (c'est le cas des attributs X et Y qui sont modifiés ou utilisé dans la fonction main(). Un membre private (privé) ne peut être référencé que par une méthode de la classe (voir ci-après).
![]() | |
![]() |
Reprenons l'exemple précédant en le modifiant un peu.
using namespace std;
class Point //on declare la classe Point
{
public:
double X;
double Y;
Point(double n, double m)
{
X=n;
Y=m;
cout«Constructeur de Point«endl;
}
~Point()
{
cout«Destructeur de Point«endl;
}
void Print()
{
cout«Point P(«X«,«Y«)«endl;
}
}; //fin de la declaration de la classe. Noter le ;
int main()
{
Point P1(4.5,2.5);
Point P2;
P2.X=3.1;
P2.Y=5.1;
P1.Print();
P2.Print();
return 0;
}
![]()
| |
![]() | |
![]()
| |
![]() | |
![]() |
Bien que très simple, la classe Point commence à être un peu difficile à lire ; nous déclarons les membres de la classe et leur code en même temps (on parle de déclaration inline). On peut alors utiliser les prototypes, comme pour les fonctions, qui permettent de séparer déclaration et code. Pour certaines opérations et certains compilateurs, ceci est même obligatoire.
using namespace std;
//
// Le prototype
//
class Point
{
public:
double X;
double Y;
Point(); //noter le ;
Point(double m, double n); //noter le ;
~Point(); //noter le ;
void Print(); //noter le ;
};
//
// le code des méthodes
//
Point::Point()
{
cout«Const. par défaut de Point«endl;
}
Point::Point(double m, double n)
{
X=m;
Y=n;
cout«Constructeur de Point«endl;
}
Point::~Point()
{
cout«Destructeur de Point«endl;
}
void Point::Print()
{
cout«Point P(«X«,«Y«)«endl;
}
//
// le programme principale
//
int main()
{
Point P1(4.5,2.5);
Point P2;
P2.X=3.1;
P2.Y=5.1;
P1.Print();
P2.Print();
return 0;
}
![]() | |
![]() | |
![]() |
Reprenons le fichier Point.h en le modifiant comme suit :
using namespace std;
class Point
{
private:
double X;
double Y;
public:
Point(){cout«Const. par défaut de Point«endl;} //declaration inline
Point(double m, double n);
void SetX(double n){X=n;} //declaration inline
void SetY(double n){Y=n;} //declaration inline
double GetX(){return X;} //declaration inline
double GetY(){return Y;} //declaration inline
void Print();
};
Remarques :
![]() | |
![]() | |
![]() | |
![]() |
Nous avons déjà utilisé les pointeurs sur des objets de base (int, float, ...). On peut aussi créer des pointeurs sur des objets d'une classe plus complexe. Considérons toujours notre classe Point et modifions la fonction main() :
{
Point P1(4.5,2.5); // P1 est un objet Point
Point *P2; // P2 est un pointeur sur un objet Point
Point *P3; // P3 est un pointeur sur un objet Point
P2= new Point(3.1,5.1); //----- ligne A ---
P3= new Point(); //----- ligne B ---
P3->SetX(1.0); //----- ligne C ---
P3->SetY(-2.5);
P1.Print(); //----- ligne D ---
P2->Print();
P3->Print();
Point *line;
line=new Point[3]; //---- ligne E ---
for(int i=0; i<3; i++) // i++ est la notation raccourcie pour i=i+1
{
line[i].SetX(2.5*i+3.2);
line[i].SetY(1.6-i);
}
for(i=0; i<3; i++) line[i].Print();
delete P2; //---- ligne F ---
delete P3;
delete [] line; //---- ligne G ---
return 0;
}
![]() | |
![]() | |
![]() | |
![]() | |
![]() | |
![]() |
Dans cette partie, vous allez vous familiariser avec la notion de classe en écrivant une classe Vecteur.
Un vecteur de R3est définit par ses 3 composantes.
Vous utiliserez le main() suivant pour tester votre classe :
{
const int n=3;
Vecteur v;
for(int i=0 ; i<n ; i++) v.Set(i,2.0*i);
double u[n]={1.5,0.,2.};
Vecteur w(u);
v.Print();
cout«w.Get(2)«endl;
return 0;
}
{
const int n=3;
double u[n]={1.,0.,0.};
double v[n]={0.,1.,0.};
double w[n]={0.,1.,1.};
Vecteur U(u),V(v),W(w);
cout«la norme de W est «W.Norme()«endl;
cout«U.V = «U.PS(V)«endl;
cout«V.W = «V.PS(W)«endl;
cout«V x W = «endl;
V.ProdVect(W).Print();
Vecteur Q=W.ProdVect(V); //------- ligne A------
cout«W x V = «endl;
Q.Print();
return 0;
}
La ligne A n'est pas si triviale qu'il n'y paraît ; en effet, le résultat de W.ProdVect(V) est un Vecteur que l'on affecte au Vecteur Q : bien que nous ne l'ayons pas écrit, C++ utilise un constructeur de recopie en recopiant chaque attribut du Vecteur W.ProdVect(V) dans le Vecteur Q (i.e., le contenu des 3 cases de double). Tant que les attributs de la classe sont des variables statiques (ici un tableau statique), le constructeur de recopie automatiquement écrit par C++ convient tout à fait. Par contre, dans le cas où on aurait à faire de l'allocation dynamique (voir ``Vecteur de taille variable''), nous devons écrire nous-même ce constructeur de recopie.
Un vecteur de Rnest définit par ses n composantes. Vous allez modifier la classe Vecteur comme suit :
On utilisera le main() suivant pour tester votre classe :
{
const int n=3;
Vecteur v(n);
for(int i=0 ; i<n ; i++) v.Set(i,2.0*i);
double u[n]={1.5,0.,2.};
Vecteur w(n,u);
v.Print();
cout«w.Get(2)«endl;
return 0;
}
Si les attributs avaient été public, rien ne nous aurait empêché d'accéder à des cases non autorisées de X.
{
cout«const. de recopie«endl;
Size=U.Size;
X= new double[Size];
for(int i=0; i<Size; i++) X[i]=U.X[i];
}
{
const int n=3;
double u[n]={1.,0.,0.};
double v[n]={0.,1.,0.};
double w[n]={0.,1.,1.};
Vecteur U(n,u),V(n,v),W(n,w);
cout«la norme de W est «W.Norme()«endl;
cout«U.V = «U.PS(V)«endl;
double ps=V.PS(W);
cout«V.W = «ps«endl;
cout«V x W = «endl;
V.ProdVect(W).Print();
Vecteur Q=W.ProdVect(V);
cout«W x V = «endl;
Q.Print();
return 0;
}
On pourra aussi voir La surdéfinition pour plus de détails.
Pour effectuer le produit scalaire entre deux vecteurs, plutôt que d'utiliser la méthode PS(Vecteur ) ci-dessus, on aimerait utiliser la syntaxe plus agréable : ps=V*W; cela est possible en redéfinissant l'opérateur * (on dit que l'opérateur * est surdéfini) de la façon suivante.
Code à rajouter dans la déclaration de la classe :
{
double ps=0;
for(int i=0 ; i<n ; i++)
ps+=V[i]*U.Get(i); // ceci est equivalent à ps=ps+V[i]*U.Get(i);
return ps;
}
{
const n=3;
double v[n]={1.,2.,3.};
double w[n]={4.,1.,1.};
Vecteur V(n,v),W(n,w);
double ps=V*W;
cout«V.W =«ps«endl;
return 0;
}
![]() | |
![]() | |
![]() (),[ ], -> , *,/,%,+,-, «,»,<, <=,>,>=,==,!=,&,^, ||,&&,|,=, +=,-=,*=,/=,%=,&=,^=,|=,«=,»=,et la virgule ,. |
Comme en mathématiques, on aimerait donner un sens -V qui est l'opposé du vecteur V. Pour cela il faut écrire:
Code à rajouter dans la déclaration de la classe :
{
return (*this)*(-1);
}
{
const n=3;
double v[n]={1.,2.,3.};
Vecteur V(n,v),W;
W=-W;
W.Print();
return 0;
}
![]() | |
![]() |
![]() Remarquez la façon économique de calculer le résultat : on utilise la multiplication externe déjà programmée. |
![]() | |
![]() |
La syntaxe permise par les méthodes ci-dessus a une petite limitation. Dans la classe Vecteur, on aimerait par exemple définir le produit externe * d'un double a avec un Vecteur V, et écrire W=a*V;
Mais d'après la disposition des objets, il faudrait que * soit un opérateur binaire de l'objet a qui se trouve à gauche . On peut cependant associer cette opération à la classe Vecteur de l'objet V en écrivant:
Code à rajouter dans la déclaration de la classe :
{
return (U*a);
}
{
const n=3;
double v[n]={1.,2.,3.};
Vecteur V(n,v),W;
double a=3.14;
W=a*V;
W.Print();
return 0;
}
![]() | |
![]() | |
![]() |
A présent, vous en savez suffisamment pour démarrer votre projet.
Pour ceux que cela intéresse, le chapitre Les
classes II
comporte un certain nombres
de compléments importants sur les classes. Dans votre projet, vous
serez amenés à utiliser du graphisme
graphisme ; mais pour l'instant il n'est pas utile de vous y plonger.
This document was generated using the LaTeX2HTML translator Version 2021 (Released January 1, 2021)
The command line arguments were:
latex2html -split 1 -html_version 3.2,math -no_navigation classeI.tex
The translation was initiated on 2025-03-11