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

Contenu supprimé Contenu ajouté
correction exemple inline/outline
DannyS712 (discussion | contributions)
m <source> -> <syntaxhighlight> (phab:T237267)
Ligne 23 :
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'''
<sourcesyntaxhighlight lang="cpp">
class A
{
Ligne 32 :
int value;
};
</syntaxhighlight>
</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/>
* '''Syntaxe'''
<sourcesyntaxhighlight lang="cpp">
class A
{
Ligne 57 :
std::cout << "Value=" << this->value << std::endl;
}
</syntaxhighlight>
</source>
 
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.
Ligne 86 :
== 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''':
<sourcesyntaxhighlight lang="cpp">
#ifndef POINT_H
#define POINT_H
Ligne 118 :
 
#endif
</syntaxhighlight>
</source>
 
Voici le fichier '''Point.cpp''':
<sourcesyntaxhighlight lang="cpp">
#include "Point.h"
#include <cmath>
Ligne 179 :
cout << "L'ordonnée vaut " << this->y << endl;
}
</syntaxhighlight>
</source>
 
Et le fichier principal '''main.cpp''':
<sourcesyntaxhighlight lang="cpp">
#include <iostream>
using namespace std;
Ligne 211 :
return 0;
}
</syntaxhighlight>
</source>
 
== Les opérateurs new et delete ==
Ligne 391 :
=== Exemple ===
*'''Fichier Fraction.h'''
<sourcesyntaxhighlight lang="cpp">
 
#define Fraction_h
Ligne 420 :
 
#endif
</syntaxhighlight>
</source>
 
*'''Fichier Fraction.cpp'''
<sourcesyntaxhighlight lang="cpp">
#include "Fraction.h"
#include <sstream>
Ligne 573 :
return r;
}
</syntaxhighlight>
</source>
 
*'''Fichier main.cpp'''
<sourcesyntaxhighlight lang="cpp">
#include <iostream>
using namespace std;
Ligne 597 :
return 0;
}
</syntaxhighlight>
</source>
 
== Héritage ==
Ligne 622 :
 
=== Syntaxe ===
<sourcesyntaxhighlight lang="cpp">
class B : ''type_héritage'' A
{
''.......''
};
</syntaxhighlight>
</source>
''type_héritage'' est l'un des mots clés d'accès <code>public</code>, <code>protected</code> ou <code>private</code>.
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'' :
Ligne 638 :
 
* Fichier '''VehiculeRoulant.h'''
<sourcesyntaxhighlight lang="cpp">
class VehiculeRoulant
{
Ligne 648 :
int position, vitesse;
};
</syntaxhighlight>
</source>
* Fichier '''VehiculeRoulant.cpp'''
<sourcesyntaxhighlight lang="cpp">
// Initialement à l'arrêt
VehiculeRoulant::VehiculeRoulant()
Ligne 666 :
vitesse++;
}
</syntaxhighlight>
</source>
* Fichier '''Automobile.h'''
<sourcesyntaxhighlight lang="cpp">
class Automobile : public VehiculeRoulant
{
Ligne 676 :
int m_places;
};
</syntaxhighlight>
</source>
* Fichier '''Automobile.cpp'''
<sourcesyntaxhighlight lang="cpp">
Automobile::Automobile(int places)
: VehiculeRoulant() // <--- pour initialiser les variables héritées
Ligne 684 :
, m_places(places) // initialiser le nombre de places
{}
</syntaxhighlight>
</source>
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.
Ligne 692 :
 
* Fichier '''VehiculeRoulant.h'''
<sourcesyntaxhighlight lang="cpp">
class VehiculeRoulant
{
Ligne 700 :
...
};
</syntaxhighlight>
</source>
* Fichier '''VehiculeRoulant.cpp'''
<sourcesyntaxhighlight lang="cpp">
void VehiculeRoulant::accelererEtAvancer()
{
Ligne 708 :
avancer();
}
</syntaxhighlight>
</source>
et que l'on modifie la façon d'avancer d'une automobile :
* Fichier '''Automobile.h'''
<sourcesyntaxhighlight lang="cpp">
class Automobile : public VehiculeRoulant
{
Ligne 719 :
...
};
</syntaxhighlight>
</source>
* Fichier '''Automobile.cpp'''
<sourcesyntaxhighlight lang="cpp">
void Automobile::avancer()
{
position += vitesse - frottementRoues;
}
</syntaxhighlight>
</source>
Alors si on utilise la méthode <code>accelererEtAvancer()</code> sur un objet de classe <code>Automobile</code>, le résultat sera incorrect, car la méthode <code>avancer()</code> appelée sera celle définie par la classe <code>VehiculeRoulant</code>.
 
Ligne 732 :
 
* Fichier '''VehiculeRoulant.h'''
<sourcesyntaxhighlight lang="cpp">
class VehiculeRoulant
{
Ligne 740 :
...
};
</syntaxhighlight>
</source>
* Fichier '''VehiculeRoulant.cpp'''
<sourcesyntaxhighlight lang="cpp">
...
 
Ligne 751 :
 
...
</syntaxhighlight>
</source>
 
Le mot clé <code>virtual</code> 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 <code>avancer()</code> de <code>VehiculeRoulant</code> à partir d'une méthode de automobile:
<sourcesyntaxhighlight lang="cpp">
void Automobile::avancer()
{
Ligne 762 :
position -= frottementRoues;
}
</syntaxhighlight>
</source>
 
=== Destructeur virtuel ===
Ligne 771 :
=== 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:
<sourcesyntaxhighlight lang="cpp">
class C : ''type_héritage'' A, ''type_héritage'' B ''...''
{
......
};
</syntaxhighlight>
</source>
 
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 <code>Base</code> ayant les sous-classes <code>H1</code> et <code>H2</code> qui en dérivent. Si l'on crée une classe <code>Finale</code> héritant à la fois de <code>H1</code> et de <code>H2</code>, alors, les instanciations des objets de classe provoqueront des appels successifs des constructeurs. D'abord <code>Finale::Finale()</code>, puis pour ses parents <code>H1::H1()</code> et <code>H2::H2()</code>, puis, le parent de <code>H1</code> <code>Base::Base()</code>, et enfin <code>Base:Base()</code> pour le parent de <code>H2</code>. On remarque alors que le constructeur de la classe <code>Base</code> est appelé à toute instanciation d'un objet de classe <code>Finale</code>. En C++, il existe un moyen d'éviter cela et de ne faire appel au constructeur de <code>Base</code> qu'une seule fois. Pour cela, il suffit d'indiquer lors des héritages le mot-clé <code>virtual</code> pour indiquer au compilateur que les constructeurs des classes multi-héritées ne doivent être appelées qu'une seule fois.
<sourcesyntaxhighlight lang="cpp">
class Base
{
Ligne 833 :
}
};
</syntaxhighlight>
</source>
 
=== Voir aussi ===
Ligne 843 :
 
=== Syntaxe ===
<sourcesyntaxhighlight lang="cpp"> virtual type nom_méthode(paramètres)=0;</sourcesyntaxhighlight>
Une telle méthode définie uniquement dans une sous-classe est nécessairement virtuelle. Le mot-clé <code>virtual</code> est donc optionnel.
<sourcesyntaxhighlight lang="cpp">type nom_méthode(paramètres)=0; // Méthode d'une sous classe encore abstraite
 
type nom_méthode(paramètres); // Méthode d'une sous classe plus abstraite</sourcesyntaxhighlight>
 
=== Exemple ===
<sourcesyntaxhighlight lang="cpp">
class Animal
{
Ligne 857 :
virtual cstring cri() = 0;
}
</syntaxhighlight>
</source>
 
== Pointeur de membre ==
Ligne 867 :
 
=== Exemple ===
<sourcesyntaxhighlight lang="cpp">
int CBox::* pBoxInt;
// pBoxInt pointe un entier dans les objets de la classe CBox
</syntaxhighlight>
</source>
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 <code>*</code> est alors remplacé par <code>''objet''.*</code> ou bien par <code>''pointeur_objet''->*</code>.
 
=== Exemple ===
<sourcesyntaxhighlight lang="cpp">
pBoxInt = &CBox::length; // désigne l'adresse du membre length de la classe
 
Ligne 883 :
CBox* pbox = new CBox(20, 24, 30);
cout << "length = " << pbox->*pBoxInt;
</syntaxhighlight>
</source>
 
== Voir aussi ==