« Programmation C++/Les classes » : différence entre les versions

Contenu supprimé Contenu ajouté
Ligne 1 :
{{Programmation C++}}
== La programmation orientée objet en C++ ==
 
=== 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.
 
Ligne 17 ⟶ 16 :
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'''
Ligne 34 ⟶ 33 :
};
</source>
==== 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''.<br/>
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''.<br/>
Ligne 62 ⟶ 61 :
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 destructeurs ===
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 <tt>new</tt>.
* Parmi les constructeurs, on retrouve une série de constructeurs particuliers:
Ligne 78 ⟶ 77 :
** explicit A(const T &); ''// constructeur à un argument
 
==== Destructeurs ====
Les destructeurs sont appelés lorsqu'une instance d'une classe est détruite. Cela arrive à la fin de l'exécution d'une fonction/méthode: 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é. Il est également appelé lorsqu'on détruit une instance grâce à <tt>delete</tt>. 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 identique à celle du constructeur, mais le destructeur est introduit par un tilde:
 
Ligne 85 ⟶ 84 :
Le destructeur ne reçoit aucun argument. Il est utile lorsque par exemple, une classe Véhicule a un pointeur sur une classe Moteur. Si la classe est détruite sans appeler un destructeur, la classe moteur ne sera pas supprimée et il y aura une fuite de mémoire, la classe Moteur restant en mémoire mais étant inaccessible.
 
=== 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''':
<source lang="cpp">
Ligne 214 ⟶ 213 :
</source>
 
=== 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 <tt>new</tt> permet de créer une nouvelle instance d'une classe <code>A</code> en écrivant :
A* pointeur=new A; // Ou bien:
Ligne 226 ⟶ 225 :
delete pointeur;
 
=== 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
{| class="wikitable" border="1"
Ligne 367 ⟶ 366 :
|}
 
==== Syntaxe ====
Les opérateurs unaires doivent être surchargés en tant que méthode de la classe.
 
Ligne 390 ⟶ 389 :
* <code>''A'' '''operator'''+(const ''A''&, const ''A'' &)</code>
 
==== Exemple ====
*'''Fichier Fraction.h'''
<source lang="cpp">
Ligne 600 ⟶ 599 :
</source>
 
=== 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 ":".
 
Ligne 622 ⟶ 621 :
Il existe en fait 3 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 ====
<source lang="cpp">
class B : ''type_héritage'' A
Ligne 636 ⟶ 635 :
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'''
Ligne 689 ⟶ 688 :
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 <tt>VehiculeRoulant</tt> :
 
Ligne 765 ⟶ 764 :
</source>
 
==== 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:
<source lang="cpp">
Ligne 836 ⟶ 835 :
</source>
 
=== 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.
 
==== Syntaxe ====
<source lang="cpp"> virtual type nom_méthode(paramètres)=0;</source>
Une telle méthode définie uniquement dans une sous-classe est nécessairement virtuelle. Le mot-clé <tt>virtual</tt> est donc optionnel.
<source lang="cpp">type nom_méthode(paramètres)=0;</source>
 
==== Exemple ====
<source lang="cpp">
class Animal
Ligne 855 ⟶ 854 :
</source>
 
=== Pointeur de membre ===
Un pointeur de membre pointe un membre d'un objet (variable ou méthode).
 
Ligne 862 ⟶ 861 :
Ce genre de pointeur occupe plus de place qu'un pointeur classique, et occupe un nombre d'octets variable selon la classe.
 
==== Exemple ====
<source lang="cpp">
int CBox::* pBoxInt;
Ligne 870 ⟶ 869 :
L'opérateur de déréférencement <code>*</code> est alors remplacé par <code>''objet''.*</code> ou bien par <code>''pointeur_objet''->*</code>.
 
==== Exemple ====
<source lang="cpp">
pBoxInt = &CBox::length; // désigne l'adresse du membre length de la classe