# Programmation C++/Les classes

* Avant-propos
* Introduction
* Compilation
* Un premier programme
* Les commentaires
* Le préprocesseur
* Les types de base et les déclarations
** Exercices
* Les opérations de base
* Les entrées-sorties
* Les pointeurs
** Exercices
* Les références
* Les tableaux
** Exercices
** Exercices sur les tableaux statiques
** Exercice : Tris des tableaux à une dimension
** Solution : Tris des tableaux à une dimension
* Les structures de contrôle
** Exercices
** Exercices if...else et switch
* Les fonctions
** Exercices
* Les structures
** Exercices
* Les classes
** Exercices
** Exercices de développement
* Les espaces de noms
* Les exceptions
* Les templates
** Exercices
* Conventions d'appel
* Les fichiers
* La librairie standard
* Expressions rationnelles
* Interfaces graphiques
* Bibliographie et liens

## Anciennes pages

* Opérateurs
* Héritage
* Polymorphisme
* Avant-propos
* Introduction
* Compilation
* Un premier programme
* Les commentaires
* Le préprocesseur
* Les types de base et les déclarations
** Exercices
* Les opérations de base
* Les entrées-sorties
* Les pointeurs
** Exercices
* Les références
* Les tableaux
** Exercices
** Exercices sur les tableaux statiques
** Exercice : Tris des tableaux à une dimension
** Solution : Tris des tableaux à une dimension
* Les structures de contrôle
** Exercices
** Exercices if...else et switch
* Les fonctions
** Exercices
* Les structures
** Exercices
* Les classes
** Exercices
** Exercices de développement
* Les espaces de noms
* Les exceptions
* Les templates
** Exercices
* Conventions d'appel
* Les fichiers
* La librairie standard
* Expressions rationnelles
* Interfaces graphiques
* Bibliographie et liens

## Anciennes pages

* Opérateurs
* Héritage
* Polymorphisme
[ Modifier le sommaire ]

## La notion de classe en programmation orientée objet

Une classe permet de regrouper dans une même entité des données et des fonctions membres (appelées aussi méthodes) permettant de manipuler ces données. La classe est la notion de base de la programmation orientée objet. Il s'agit en fait d'une évolution de la notion de structure, qui apporte de nouvelles notions orientées objet absolument fondamentales. Un objet est un élément d'une certaine classe : on parle de l'instance d'une classe.

## L'encapsulation en C++

L'encapsulation est un mécanisme qui interdit d'accéder à certaines données depuis l'extérieur de la classe. Ainsi, un utilisateur de la classe ne pourra pas accéder à tous les éléments de celles-ci. Il sera obligé d'utiliser certaines fonctions membres de la classe (celles qui sont publiques). L'avantage de cette restriction est qu'il empêche par exemple un utilisateur de la classe de mettre les données dans un état incohérent. Vue de l'extérieur, la classe apparaît comme une boîte noire, qui a un certain comportement à laquelle on ne peut accéder que par les méthodes publiques. Cette notion est extrêmement puissante et permet d'éviter de nombreux effets de bord.
L'encapsulation permet de distinguer très nettement ce que fait la classe et sa sémantique précise, de la manière dont on l'implémente. Cette réflexion permet de répondre dans un premier temps à la question Comment utilise-t-on la classe ? Ce n'est que dans un second temps que l'aspect technique entre en jeu et que le programmeur doit répondre à la question Comment vais-je programmer les fonctionnalités de la classe qui ont été spécifiées ? L'encapsulation permet de faire abstraction du fonctionnement interne (c'est-à-dire, l'implémentation) d'une classe et ainsi de ne se préoccuper que des services rendus par celle-ci.
Le C++ implémente l'encapsulation en permettant de déclarer les membres d'une classe avec l'un des mots réservés public, private et protected. Ainsi, lorsqu'un membre est déclaré:
* public, il sera accessible depuis n'importe quelle fonction.
* private, il sera uniquement accessible d'une part, depuis les fonctions qui sont membres de la classe et, d'autre part, depuis les fonctions et classes autorisées explicitement par la classe (par l'intermédiaire du mot réservé friend). Ces dernières fonctions/classes sont appelées fonctions/classes amies de la classe.
* protected, il aura les mêmes restrictions que s'il était déclaré private, mais il sera en revanche accessible par les classes filles.
Le C++ n'impose pas l'encapsulation des membres dans leurs classes. On pourrait donc déclarer tous les membres publiques, mais en perdant une partie des bénéfices apportés par la programmation orientée objet. Il est de bon usage de déclarer toutes les données privées, ou au moins protégées, et de rendre publiques les méthodes agissant sur ces données. Ceci permet de cacher les détails de l'implémentation de la classe.

## Les fonctions membres

Parmi les fonctions membres, on distingue :
* les Accesseurs: ce sont des fonctions membres qui ne modifient pas l'état de l'objet. Le C++ permet de déclarer une fonction membre const, indiquant au compilateur qu'elle ne modifie pas l'état de l'objet. Si l'implémentation de la fonction const tente de modifier une variable membre de la classe, le compilateur signalera une erreur. Seules les fonctions membres const peuvent être invoquées depuis un objet déclaré const. Une tentative d'appel à une fonction membre non const depuis un objet const échouerait à la compilation.
* les Modificateurs : ce sont des fonctions membres qui peuvent modifier l'état de l'objet. Dans ce cas, on omet le const dans la déclaration. Si une fonction membre déclarée non const ne modifie pas l'état de l'objet, il y a lieu de se demander s'il ne faudrait pas la déclarer const.

### Déclaration

La déclaration d'une fonction membre se fait de la même manière qu'une fonction régulière mais au sein de la portée de la classe. En outre, on pourra la suffixer du mot clé const pour signifier qu'il s'agit d'un accesseur.
* Syntaxe

### Définition

Dans le cas d'une définition au sein de la classe même, elle est faite comme une fonction régulière. Dans ce cas, la fonction membre est automatiquement considérée comme inline. Dans le cas d'une définition en dehors de la classe, il faudra préfixer le nom de la fonction membre par le nom de la classe suivi de l'opérateur de portée (::). Dans ce cas, elle est automatiquement considérée comme non inline.
* Syntaxe
En règle générale, les fonctions membres non inline devront être déclarées dans le fichier source .cpp de la classe, pour éviter d'avoir plusieurs fois la même définition au moment de l'édition des liens, et les fonctions membres inline devront être déclarées dans le fichier d'entête .hpp de la classe, pour permettre au compilateur de générer le code à chaque appel de la fonction.

## Constructeurs et destructeur

Les constructeurs et destructeur d'une classe sont des fonctions membres particulières de cette classe.

### Constructeurs

* Lorsqu'on crée une nouvelle instance d'une classe, les données membres de cette instance ne sont pas initialisées par défaut. Un constructeur est une fonction membre qui sera appelée au moment de la création d'une nouvelle instance d'une classe. Il peut y avoir plusieurs constructeurs d'une classe avec différents paramètres qui serviront à initialiser notre classe. Le constructeur d'une classe A est appelé automatiquement lorsqu'on crée une instance en écrivant : A toto; ou encore A toto(6,9);. Ils sont également appelés lorsqu'une instance est créée grâce à l'opérateur new.
* Parmi les constructeurs, on retrouve une série de constructeurs particuliers:
** Le constructeur par défaut : c'est le constructeur sans argument. Si aucun constructeur n'est présent explicitement dans la classe, il est généré par le compilateur.
** Le constructeur de copie : c'est un constructeur à un argument dont le type est celui de la classe. En général, l'argument est passé par référence constante. Plus rarement, il peut l'être par valeur ou référence non constante. Si il n'est pas explicitement présent dans la classe, il est généré par le compilateur. Notons encore que:
*** Si un constructeur de copie est fourni dans une classe, il sera très probable de devoir fournir l'opérateur d'assignation avec le même type d'argument.
*** Il est possible de rendre une classe non copiable en privatisant la déclaration du constructeur de copie et de l'opérateur d'assignation. Dans ce cas, il n'est pas nécessaire (recommandé) de fournir leur définition.
** Les constructeurs de conversion implicite : il s'agit de constructeurs à un seul argument dont le type est différent de celui de la classe et qui ne sont pas déclarés explicit. Ces constructeurs rendent possible la conversion implicte du type d'argument vers le type de la classe.
* Syntaxe : (Pour une classe A)
** A; // constructeur par défaut
** A(const A &); // constructeur de copie
** A(const T &); // constructeur de conversion implicite
** explicit A(const T &); // constructeur à un argument

### Destructeur

Le destructeur d'une classe est appelé lorsqu'une de ses instances doit être détruite. Cela arrive à la fin de l'exécution d'une fonction/méthode lors d'une allocation dynamique : si vous avez déclaré, localement à une fonction/méthode, un élément d'une classe A par A toto; alors à la fin de l'exécution de la fonction/méthode, le destructeur de la classe est appelé automatiquement. Il est également appelé lorsqu'on détruit une instance avec delete.
Il ne peut y avoir qu'un seul destructeur pour une même classe. S'il n'est pas explicitement présent dans la classe, il sera généré par le compilateur. La syntaxe est similaire à celle du constructeur, à ceci près que le destructeur est introduit par un tilde et qu'il ne reçoit aucun argument :
~A;
Un appel explicite avec delete est nécessaire lorsqu'une instance a été allouée avec new. Dans le cas contraire, si le pointeur est détruit sans appeler le destructeur, l'instance ne sera pas réellement supprimée et persistera en mémoire mais sans être accessible, ce qui occasionnera ce qu'on appelle une fuite de mémoire.

## Exemples de classes

Dans cet exemple de classe, les fichiers Point.h, Point.cpp et main.cpp vont vous être exposés. Tout d'abord, Point.h:
Voici le fichier Point.cpp:
Et le fichier principal main.cpp:

## Les opérateurs new et delete

Il est parfois intéressant de créer dynamiquement de nouvelles instances d'une classe. Cela s'avère indispensable lorsque vous manipulez certaines structures de données complexes. L'opérateur new permet de créer une nouvelle instance d'une classe A en écrivant :
Si le constructeur de la classe A prend des arguments, la syntaxe devient:
La variable pointeur doit alors être du type A*.
La libération se fait en utilisant l'opérateur delete et le destructeur de la classe est appelé:

## Surcharge d'opérateurs
### Présentation

Pour certaines classes, la notion d'addition ou de multiplication est totalement naturelle. La surcharge d'opérateurs permet d'écrire directement U+V lorsqu'on veut additionner deux instances U et V d'une même classe A.

### Opérateurs surchargeables

* Les opérateurs unaires : ce sont des opérateurs qui s'appliquent sans argument supplémentaire
| opérateur | définition |
| + | opérateur signe + |
| - | opérateur négation |
| * | opérateur déréférencement |
| & | opérateur adresse |
| -> | opérateur indirection |
| ~ | opérateur négation binaire |
| ++ | opérateur post/pré-incrémentation |
| -- | opérateur post/pré-décrémentation |
* Les opérateurs binaires: ce sont des opérateurs qui s'appliquent moyennant un argument supplémentaire
| opérateur | définition |
| = | opérateur assignation |
| + | opérateur addition |
| += | opérateur addition/assignation |
| - | opérateur soustraction |
| -= | opérateur soustraction/assignation |
| * | opérateur multiplication |
| *= | opérateur multiplication/assignation |
| / | opérateur division |
| /= | opérateur division/assignation |
| % | opérateur modulo |
| %= | opérateur modulo/assignation |
| ^ | opérateur ou exclusif |
| ^= | opérateur ou exclusif/assignation |
| & | opérateur et binaire |
| &= | opérateur et binaire/assignation |
| | | opérateur ou binaire |
| |= | opérateur ou binaire/assignation |
| < | opérateur plus petit que |
| <= | opérateur plus petit ou égal |
| > | opérateur plus grand que |
| >= | opérateur plus grand ou égal |
| << | opérateur de décalage à gauche |
| <<= | opérateur de décalage à gauche/assignation |
| >> | opérateur de décalage à droite |
| >>= | opérateur de décalage à droite/assignation |
| == | opérateur d'égalité |
| != | opérateur d'inégalité |
| && | opérateur et logique |
| || | opérateur ou logique |
| | opérateur indexation |
| , | opérateur enchaînement |
| | opérateur fonction |

### Syntaxe

Les opérateurs unaires doivent être surchargés en tant que méthode de la classe.
La signature générique au sein de la classe est
* type_retour operator @; // pour les méthodes non const
* type_retour operator @ const; // pour les méthodes const
où @ est l'opérateur à surcharger: + - * / ...
Excepté pour certains d'entre eux, les opérateurs binaires peuvent être surchargés en tant que méthode de la classe ou comme une fonction à 2 arguments.
La signature générique est la suivante
* type_retour operator @(Arg); // pour les méthodes non const
* type_retour operator @(Arg) const; // pour les méthodes const
* type_retour operator @(Arg1, Arg2) const; // pour les fonctions
où @ est l'opérateur à surcharger: + - * / ...
Pour surcharger l'opérateur+ de la classe A, il suffit de créer une méthode:
* A operator+(const A& a) const;
ou encore une fonction externe à la classe:
* A operator+(const A&, const A &)

### Exemple

* Fichier Fraction.h
* Fichier Fraction.cpp
* Fichier main.cpp

## Héritage
### Présentation

Dans la conception orientée objet, la généralisation consiste à modéliser des concepts communs à un ensemble d'autres concepts. Les autres concepts deviennent dès lors des spécialisations de la généralisation. Cette manière de modéliser s'appelle l'héritage, car les spécialisations héritent de la généralisation. En C++, les classes peuvent hériter d'autres classes et la relation d'héritage est exprimée à l'aide de l'opérateur de dérivation ":".
À partir d'une classe A, on peut créer une classe B qui possède toutes les caractéristiques de la classe A, à laquelle on ajoute un certain nombre de méthodes qui sont spécifiques à B. Cette notion orientée objet fondamentale s'appelle l'héritage.
On dit que :
* la classe B hérite de la classe A ;
* la classe B est une sous-classe de la classe A ;
* la classe B spécialise la classe A ;
* la classe B étend la classe A ;
* la classe B dérive de la classe A (cette notation n'est toutefois pas appréciée de tous, mais a l'avantage d'éviter les ambiguïtés à propos de super classe et sous classe) ;
* la classe A est une super-classe de la classe B ;
* la classe A généralise la classe B.
On dit aussi que:
* la classe A est une classe de base
* la classe B est une classe dérivée
Il existe en fait trois types d'héritage public, private ou protected qui permet de spécifier si oui ou non une méthode de la classe B peut modifier une donnée membre de la classe A, selon qu'elle soit public, private ou protected.

### Syntaxe

type_héritage est l'un des mots clés d'accès public, protected ou private. Les membres de A sont alors à la fois accessibles à la classe dérivée B et en dehors de ces classes selon la valeur utilisée pour type_héritage :
* Quand type_héritage est public, les membres de la classe A conservent leur accès (privé, protégé et public).
* Quand type_héritage est protected, les membres publics de la classe A deviennent protégés.
* Quand type_héritage est private, les membres publics et protégés de la classe A deviennent privés.
Pour résumer, type_héritage ne peut que restreindre l'accès des membres de la classe A, ou le conserver.

### Exemple

* Fichier VehiculeRoulant.h
* Fichier VehiculeRoulant.cpp
* Fichier Automobile.h
* Fichier Automobile.cpp
L'appel explicite au constructeur de la classe de base dans cet exemple n'est pas nécessaire car par défaut, le constructeur (sans paramètres) est appelé. Dans le cas où il est nécessaire d'appeler un autre constructeur, il faut le faire dans la liste d'initialisation du constructeur de la classe dérivée.

### Méthodes virtuelles

Dans l'exemple précédent, si l'on ajoute une autre méthode à la classe VehiculeRoulant :
* Fichier VehiculeRoulant.h
* Fichier VehiculeRoulant.cpp
et que l'on modifie la façon d'avancer d'une automobile :
* Fichier Automobile.h
* Fichier Automobile.cpp
Alors si on utilise la méthode accelererEtAvancer sur un objet de classe Automobile, le résultat sera incorrect, car la méthode avancer appelée sera celle définie par la classe VehiculeRoulant.
Pour que ce soit celle de la classe Automobile qui soit appelée, il faut que la méthode avancer soit définie comme virtuelle dans la classe de base VehiculeRoulant :
* Fichier VehiculeRoulant.h
* Fichier VehiculeRoulant.cpp
Le mot clé virtual indique une méthode virtuelle, c'est-à-dire que son adresse n'est pas fixe, mais dépend de la classe de l'objet (le compilateur construit une table interne des méthodes virtuelles pour chaque classe). Il est possible d'accéder à une méthode précise de la table des méthodes virtuelles, en indiquant la classe de la méthode à appeler. Par exemple, pour appeler la méthode avancer de VehiculeRoulant à partir d'une méthode de automobile:

### Destructeur virtuel

Il est important d'utiliser un destructeur virtuel pour toute classe qui sera dérivée. Dans le cas contraire seul celui de la classe de base est appelé, sans libérer les membres des sous-classes.
Ne pas déclarer un destructeur comme virtuel interdit donc de dériver cette classe.

### Héritage multiple

L'héritage multiple permet à une classe d'hériter de plusieurs super-classes. Ceci permet d'intégrer dans la sous-classe plusieurs concept d'abstractions qui caractérisent cette sous-classe. Pour cela, il suffit de déclarer les super-classes les unes après les autres. Si une classe C hérite de A et de B, la syntaxe sera la suivante:
Par exemple, si l'on possède une classe décrivant un véhicule roulant, et une classe décrivant un navire, on pourrait créer une classe décrivant un véhicule amphibie qui hériterait des propriétés et méthodes de véhicule roulant et de navire.
L'héritage multiple peut poser des problèmes de définitions multiples de méthodes virtuelles. Supposons que l'on ait une classe Base ayant les sous-classes H1 et H2 qui en dérivent. Si l'on crée une classe Finale héritant à la fois de H1 et de H2, alors, les instanciations des objets de classe provoqueront des appels successifs des constructeurs. D'abord Finale::Finale, puis pour ses parents H1::H1 et H2::H2, puis, le parent de H1 Base::Base, et enfin Base:Base pour le parent de H2. On remarque alors que le constructeur de la classe Base est appelé à toute instanciation d'un objet de classe Finale. En C++, il existe un moyen d'éviter cela et de ne faire appel au constructeur de Base qu'une seule fois. Pour cela, il suffit d'indiquer lors des héritages le mot-clé virtual pour indiquer au compilateur que les constructeurs des classes multi-héritées ne doivent être appelées qu'une seule fois.

### Voir aussi

* Exercices

## Classes abstraites
### Présentation

Une classe abstraite est une classe pour laquelle on a défini une méthode mais on a explicitement indiqué qu'on ne fournira aucune implémentation de cette méthode. Il est interdit de créer une instance d'une classe abstraite. Ce mécanisme est extrêmement puissant pour manipuler des concepts abstraits. On peut même avoir une classe pour laquelle toutes les méthodes sont abstraites, on parle alors de classe abstraite pure.
Une classe héritant d'une classe abstraite doit fournir une implémentation des méthodes abstraites ou bien elle sera à son tour une classe abstraite.

### Syntaxe

Une telle méthode définie uniquement dans une sous-classe est nécessairement virtuelle. Le mot-clé virtual est donc optionnel.

### Exemple
## Pointeur de membre

Un pointeur de membre pointe un membre d'un objet (variable ou méthode).
Ce genre de pointeur, rarement utilisé, possède une syntaxe spéciale. L'étoile * est remplacée par class::* signifiant un pointeur sur un membre de cette classe, ou d'une classe dérivée. Ce genre de pointeur occupe plus de place qu'un pointeur classique, et occupe un nombre d'octets variable selon la classe.

### Exemple

L'utilisation d'un tel pointeur nécessite un objet de la classe concernée (ou classe dérivée). L'opérateur de déréférencement * est alors remplacé par objet.* ou bien par pointeur_objet->*.

### Exemple
## Voir aussi

* Exercices de développement