1. CLASSE OU STRUCTURE
 

Nous proposons ici l'étude d'une classe très simple, afin de montrer les différences fondamentales entre une classe en C++ et une structure en C. L'exemple de classe suivant nous permet de réintroduire les notions de construction et de destruction, ainsi que les opérations d'affectation et de copie.

class Complexe {
 double _x;
 double _y;
};

Vous noterez que, par défaut, les membres d'une classe sont protégés, leur accès de l'extérieur de la classe est impossible, c'est le principe fondamental d'encapsulation: le maximum de détails sur l'implémentation d'une classe doit être masqué, cela permet une certaine indépendance qui facilite la maintenance de la classe. L'accès (et le contrôle) aux attributs est généralement permis en lecture et/ou en écriture par des méthodes qu'on nomme des accesseurs (cf. chapitre suivant).

 
CONSTRUCTION
 

Pour créer un objet de la classe Complexe, deux solutions sont possibles. La première consiste à déclarer simplement une variable.

Complexe c; (1)

La seconde possibilité est d'allouer dynamiquement un objet.

Complexe * pc = new Complexe(); (2)

Dans les deux cas, le constructeur dit par défaut est appelé. Le rôle de cette méthode est de préciser comment construire un objet standard (i.e. avec des valeurs par défaut) de la classe, sans que l'utilisateur ne précise aucun paramètre. Dans notre exemple, comme nous n'avons pas écrit de constructeur, un constructeur par défaut est alors automatiquement fourni, équivalent à ce qui suit.

Complexe::Complexe(void) : _x(), _y() {} (3)

Rappelons que créer (dit également instancier) un objet se déroule en deux phases. La première consiste en l'allocation de l'espace mémoire nécessaire à l'élément, ce qui est effectué sur la pile et automatiquement dans le cas (1), ou dynamiquement dans la mémoire centrale dans le cas (2). Une fois l'allocation réalisée, un constructeur est appelé, et dans notre cas il s'agit du constructeur par défaut. Cependant, avant d'exécuter le constructeur même, la construction des attributs de l'objet doit être réalisée. C'est le propos du symbole : qui indique qu'on se trouve entre l'allocation de l'objet et l'appel à son constructeur. Si l'on ne précise rien, les attributs sont construits par défaut comme explicité par (3), mais il est possible de préciser d'autres valeurs pour les attributs, comme dans l'exemple suivant.

Complexe::Complexe(void) : _x(5.0), _y(10.0) {}

Remarquons que si l'on change l'ordre de construction des attributs, le compilateur émet alors un avertissement et les replace dans l'ordre de déclaration. En résumé, les attributs sont toujours construits selon leur ordre de déclaration. Il faut également noter que dans le cas où les attributs sont d'un type primitif (entier, flottant, booléen...), ils ne sont pas initialisés si l'on ne précise pas de valeur au constructeur.

Il est possible de proposer autant de constructeurs qu'on souhaite (à condition qu'ils aient une signature - i.e. la liste des types de leurs arguments - différente). Cela permet de fournir tous les paramètres désirés à la construction d'un objet.

Complexe::Complexe(double x, double y) : _x(x), _y(y) {}

Ainsi, nous pouvons créer un objet de la manière suivante.

Complexe c(5.0,10.0);

Il est à noter que, dès qu'on définit un constructeur, le constructeur par défaut n'est plus fourni automatiquement par le compilateur, et qu'il faut donc le réécrire si nécessaire.

 
CONSTRUCTEUR DE COPIE
 

Le compilateur fournit également automatiquement un constructeur dit de copie, qui est disponible même si on définit d'autres constructeurs (contrairement au constructeur par défaut). Son rôle est d'initialiser un nouvel objet de la classe en copiant une instance existante. Dans notre exemple, le constructeur de copie fourni par défaut est équivalent à ce qui suit.

Complexe::Complexe(const Complexe & c) : _x(c._x), _y(c._y) {}

Cela signifie que, par défaut, le constructeur de copie appelle le constructeur de copie de chaque attribut pour recopier l'objet. Voici un exemple d'utilisation du constructeur de copie.

Complexe c1(5.0,10.0);
Complexe c2(c1);
Complexe c3 = c1;

La seconde ligne appelle le constructeur de copie de la classe. Les attributs de l'instance c1 sont alors recopiés pour initialiser c2. La troisième ligne est équivalente à la seconde. Même si le signe = apparaît ici, il ne s'agit pas d'une affectation mais bien d'une construction par copie.

 
OPERATEUR D'AFFECTATION
 

A partir de l'exemple précédent, considérons l'instruction suivante, en dehors de toute déclaration: c2=c3. Il s'agit cette fois-ci d'une affectation, autrement dit de la recopie d'une instance. L'opérateur qui assure cette opération, dit d'affectation, est fourni par défaut et est équivalent à ce qui suit.

Complexe & Complexe::operator=(const Complexe & c) {
 _x=c._x;
 _y=c._y;
}

Cet opérateur appelle automatiquement l'opérateur d'affectation de chacun des attributs afin de recopier totalement l'objet en paramètre. Dans notre exemple, l'affectation est équivalente à l'appel suivant.

c2.operator=(c3);

 
DESTRUCTION
 

Au cours d'un programme, les objets créés sont naturellement amenés à être supprimés. Cette procédure se déroule en deux étapes (à l'image de la création d'un objet). Tout d'abord, l'appel au destructeur de l'objet et ensuite la libération de la mémoire occupée par l'objet. Dans le cas de la création d'une variable (cf. le cas (1) dans la section sur la construction), la suppression s'effectue à la fin du bloc où a été déclarée la variable. Dans le cas d'une allocation dynamique (cf. le cas (2) dans la section sur la construction), l'utilisateur doit décider du moment de la destruction par la commande suivante.

delete pc;

Il faut noter, à l'image de la création d'un objet, qu'entre l'appel au destructeur et la libération de la mémoire, les attributs sont détruits dans l'ordre inverse de leur déclaration. A l'opposé de la construction, l'utilisateur ne peut pas intervenir dans ce processus. Un destructeur est toujours fourni par défaut, mais il n'effectue aucune opération particulière.

Complexe::~Complexe(void) {}

 
 
Copyright (c) 1999-2016 - Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
La permission est accordée de copier, distribuer et/ou modifier ce document sous les termes de la licence GNU Free Documentation License, Version 1.1 ou toute version ultérieure publiée par la fondation Free Software Foundation. Voir cette licence pour plus de détails (http://www.gnu.org).