Comment ça marche
Nous avons commencé à réaliser une classe Point qui permet la recopie d’objet. Mais jusqu’à maintenant, nous avons été obligés de créer des méthodes telles que "
Coincide
" qui vérifie que deux points sont égaux. Nous aurions pu également réaliser une fonction membre qui ajoute un point à un autre :
class Point
{
int x;
int y;
public :
Point();
Point(int a, int b);
Point(const Point & pt);
void Ajoute(const Point & pt);
... // D’éventuelles autres méthodes
};
{
int x;
int y;
public :
Point();
Point(int a, int b);
Point(const Point & pt);
void Ajoute(const Point & pt);
... // D’éventuelles autres méthodes
};
Vous sentez alors qu’il serait très appréciable et plus naturel de pouvoir faire quelque chose comme :
Point a(1,2);
Point b(3,4);
Point c;
c = a + b;
Point b(3,4);
Point c;
c = a + b;
C++ permet de réaliser une surdéfinition des opérateurs de base, comme
"+", "-", "*", "/", "&", "^"
, etc. La liberté étant totale, vous pouvez faire réellement ce que vous voulez, par exemple une soustraction pour l’opérateur d’addition et inversement. Mais il est clair qu’il est plus que conseillé de respecter la signification de chacun de ces opérateurs.Opérateurs simples
Mettre en oeuvre les opérateurs simples est une opération assez rapide. Un exemple est utile pour vous montrer comment on fait :
class Point
{
int x;
int y;
public :
Point();
Point(int a, int b);
Point(const Point & pt);
Point operator+(const Point & a)
{
Point p;
p.x = a.x + x;
p.y = a.y + y;
return p;
}
... // D’éventuelles autres méthodes
};
{
int x;
int y;
public :
Point();
Point(int a, int b);
Point(const Point & pt);
Point operator+(const Point & a)
{
Point p;
p.x = a.x + x;
p.y = a.y + y;
return p;
}
... // D’éventuelles autres méthodes
};
Des explications sont nécessaires. Tout d’abord, vous remarquerez le "
const Point & a
". Ceci signifie que l’on passe un point en paramètre (l’autre point de l’addition est en fait la classe appelante elle-même). Ce dernier est transmis par référence, afin d’éviter une recopie, lourde et moins rapide. Le "const
" est optionnel mais permet d’éviter de modifier les paramètres et également autorise l’utilisation d’objets constants.
L’opérateur rend un Point. En fait, on rend en fin de méthode le point qui a été créé temporairement au départ et qui contient la somme des deux paramètres. Plus exactement, on rend une copie de cet objet, le retour de la fonction étant "
Point
", et non "Point&
" ou "Point*
". Ceci est normal. Il faut savoir que l’objet créé dans la méthode sera automatiquement détruit à la fin de cette dernière. On ne peut bien évidemment pas rendre l’adresse d’un objet qui sera détruit ! D’où la nécessité d’une recopie.
Ce que nous avons mis en oeuvre pour l’opérateur d’addition, nous pouvons en faire de même pour tous les autres opérateurs "simples". Quelques exemples :
class Point
{
int x;
int y;
public :
Point(){}
Point(int a, int b){ x=a; y=b; }
Point operator +(const Point & a);
Point operator -(const Point & a);
int operator==(const Point & p);
void Affiche()
{
// "this" est un pointeur sur la classe même
cout << this << "->" << x << ", " << y << endl;
}
};
Point Point::operator +(const Point & a)
{ // Addition de 2 points
Point p;
p.x = x + a.x;
p.y = y + a.y;
return p;
}
Point Point::operator -(const Point & a)
{ // Soustraction de 2 points
Point p;
p.x = x - a.x;
p.y = y - a.y;
return p;
}
int Point::operator==(const Point & p)
{ // Egalité de 2 points (remplace "Coincide")
if( x==p.x && y==p.y )
return 1;
else
return 0;
}
void main()
{
Point p(1,2);
p.Affiche();
Point pp(3,4);
pp.Affiche();
Point ppp = p+pp;
ppp.Affiche();
if( p==pp )
cout << "p==pp" << endl;
else
cout << "p!=pp" << endl;
p = ppp;
p.Affiche();
pp = p-ppp;
pp.Affiche();
if( p==ppp )
cout << "p==ppp" << endl;
else
cout << "p!=ppp" << endl;
}
{
int x;
int y;
public :
Point(){}
Point(int a, int b){ x=a; y=b; }
Point operator +(const Point & a);
Point operator -(const Point & a);
int operator==(const Point & p);
void Affiche()
{
// "this" est un pointeur sur la classe même
cout << this << "->" << x << ", " << y << endl;
}
};
Point Point::operator +(const Point & a)
{ // Addition de 2 points
Point p;
p.x = x + a.x;
p.y = y + a.y;
return p;
}
Point Point::operator -(const Point & a)
{ // Soustraction de 2 points
Point p;
p.x = x - a.x;
p.y = y - a.y;
return p;
}
int Point::operator==(const Point & p)
{ // Egalité de 2 points (remplace "Coincide")
if( x==p.x && y==p.y )
return 1;
else
return 0;
}
void main()
{
Point p(1,2);
p.Affiche();
Point pp(3,4);
pp.Affiche();
Point ppp = p+pp;
ppp.Affiche();
if( p==pp )
cout << "p==pp" << endl;
else
cout << "p!=pp" << endl;
p = ppp;
p.Affiche();
pp = p-ppp;
pp.Affiche();
if( p==ppp )
cout << "p==ppp" << endl;
else
cout << "p!=ppp" << endl;
}
Opérateur d’affectation
L’opérateur d’affectation "
=
" représente un cas particulier. En effet, nous retrouvons le même problème que lors de la construction par recopie : il est toujours possible d’effectuer une affectation entre deux objets (de même type), mais que se passe-t-il s’ils contiennent des pointeurs (cf. problématique de recopie) ! C’est pourquoi il est souvent important d’implémenter ce type d’opérateur.
Il n’est pas plus difficile à mettre en oeuvre :
#include <iostream.h>
class Vecteur
{
int *pVecteur;
int nTaille;
public :
Vecteur(int);
Vecteur(const Vecteur &);
~Vecteur();
int GetAt(int ind)
{
if( ind>=0 && ind<nTaille )
return pVecteur[ind];
else
return 0;
}
void SetAt(int ind, int val)
{
if( ind>=0 && ind<nTaille )
pVecteur[ind]=val;
}
int Size(){ return nTaille; }
Vecteur& operator =(const Vecteur & v);
};
Vecteur::Vecteur(int Taille)
{
nTaille = Taille;
pVecteur = new int[nTaille];
}
Vecteur::Vecteur(const Vecteur & v)
{
nTaille = v.nTaille;
pVecteur = new int[nTaille];
for( int i=0; i<nTaille; i++ )
pVecteur[i]=v.pVecteur[i];
}
Vecteur::~Vecteur()
{
delete []pVecteur;
}
Vecteur& Vecteur::operator =(const Vecteur & v)
{
// On vérifie que les objets ne sont pas les mêmes !
if( this != &v )
{
delete []pVecteur; // Effacement du vecteur
nTaille = v.nTaille;
pVecteur = new int[nTaille]; // Allocation
// Recopie des valeurs
for( int i=0; i<nTaille; i++ )
pVecteur[i]=v.pVecteur[i];
}
return *this;
}
void main()
{
Vecteur *v;
v = new Vecteur(10);
for( int i=0; i<v->Size(); i++ )
v->SetAt(i, i*i-i);
Vecteur vv(5);
vv = *v;
vv.SetAt(0, 13);
vv.SetAt(1, 13);
vv.SetAt(2, 13);
for( i=0; i<v->Size(); i++ )
cout << v->GetAt(i) << " ";
cout << endl;
for( i=0; i<vv.Size(); i++ )
cout << vv.GetAt(i) << " ";
cout << endl;
delete v;
}
class Vecteur
{
int *pVecteur;
int nTaille;
public :
Vecteur(int);
Vecteur(const Vecteur &);
~Vecteur();
int GetAt(int ind)
{
if( ind>=0 && ind<nTaille )
return pVecteur[ind];
else
return 0;
}
void SetAt(int ind, int val)
{
if( ind>=0 && ind<nTaille )
pVecteur[ind]=val;
}
int Size(){ return nTaille; }
Vecteur& operator =(const Vecteur & v);
};
Vecteur::Vecteur(int Taille)
{
nTaille = Taille;
pVecteur = new int[nTaille];
}
Vecteur::Vecteur(const Vecteur & v)
{
nTaille = v.nTaille;
pVecteur = new int[nTaille];
for( int i=0; i<nTaille; i++ )
pVecteur[i]=v.pVecteur[i];
}
Vecteur::~Vecteur()
{
delete []pVecteur;
}
Vecteur& Vecteur::operator =(const Vecteur & v)
{
// On vérifie que les objets ne sont pas les mêmes !
if( this != &v )
{
delete []pVecteur; // Effacement du vecteur
nTaille = v.nTaille;
pVecteur = new int[nTaille]; // Allocation
// Recopie des valeurs
for( int i=0; i<nTaille; i++ )
pVecteur[i]=v.pVecteur[i];
}
return *this;
}
void main()
{
Vecteur *v;
v = new Vecteur(10);
for( int i=0; i<v->Size(); i++ )
v->SetAt(i, i*i-i);
Vecteur vv(5);
vv = *v;
vv.SetAt(0, 13);
vv.SetAt(1, 13);
vv.SetAt(2, 13);
for( i=0; i<v->Size(); i++ )
cout << v->GetAt(i) << " ";
cout << endl;
for( i=0; i<vv.Size(); i++ )
cout << vv.GetAt(i) << " ";
cout << endl;
delete v;
}
Cette fois-ci, nous rendons par contre une référence sur l’objet, car nous devons rendre la classe elle-même et non une copie, comme vous pouvez le comprendre.
Aucun commentaire:
Enregistrer un commentaire