« Programmation C++/Les classes » : différence entre les versions
Contenu supprimé Contenu ajouté
correction exemple inline/outline |
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'''
<
class A
{
Ligne 32 :
int value;
};
</syntaxhighlight>
=== 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'''
<
class A
{
Ligne 57 :
std::cout << "Value=" << this->value << std::endl;
}
</syntaxhighlight>
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''':
<
#ifndef POINT_H
#define POINT_H
Ligne 118 :
#endif
</syntaxhighlight>
Voici le fichier '''Point.cpp''':
<
#include "Point.h"
#include <cmath>
Ligne 179 :
cout << "L'ordonnée vaut " << this->y << endl;
}
</syntaxhighlight>
Et le fichier principal '''main.cpp''':
<
#include <iostream>
using namespace std;
Ligne 211 :
return 0;
}
</syntaxhighlight>
== Les opérateurs new et delete ==
Ligne 391 :
=== Exemple ===
*'''Fichier Fraction.h'''
<
#define Fraction_h
Ligne 420 :
#endif
</syntaxhighlight>
*'''Fichier Fraction.cpp'''
<
#include "Fraction.h"
#include <sstream>
Ligne 573 :
return r;
}
</syntaxhighlight>
*'''Fichier main.cpp'''
<
#include <iostream>
using namespace std;
Ligne 597 :
return 0;
}
</syntaxhighlight>
== Héritage ==
Ligne 622 :
=== Syntaxe ===
<
class B : ''type_héritage'' A
{
''.......''
};
</syntaxhighlight>
''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'''
<
class VehiculeRoulant
{
Ligne 648 :
int position, vitesse;
};
</syntaxhighlight>
* Fichier '''VehiculeRoulant.cpp'''
<
// Initialement à l'arrêt
VehiculeRoulant::VehiculeRoulant()
Ligne 666 :
vitesse++;
}
</syntaxhighlight>
* Fichier '''Automobile.h'''
<
class Automobile : public VehiculeRoulant
{
Ligne 676 :
int m_places;
};
</syntaxhighlight>
* Fichier '''Automobile.cpp'''
<
Automobile::Automobile(int places)
: VehiculeRoulant() // <--- pour initialiser les variables héritées
Ligne 684 :
, m_places(places) // initialiser le nombre de places
{}
</syntaxhighlight>
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'''
<
class VehiculeRoulant
{
Ligne 700 :
...
};
</syntaxhighlight>
* Fichier '''VehiculeRoulant.cpp'''
<
void VehiculeRoulant::accelererEtAvancer()
{
Ligne 708 :
avancer();
}
</syntaxhighlight>
et que l'on modifie la façon d'avancer d'une automobile :
* Fichier '''Automobile.h'''
<
class Automobile : public VehiculeRoulant
{
Ligne 719 :
...
};
</syntaxhighlight>
* Fichier '''Automobile.cpp'''
<
void Automobile::avancer()
{
position += vitesse - frottementRoues;
}
</syntaxhighlight>
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'''
<
class VehiculeRoulant
{
Ligne 740 :
...
};
</syntaxhighlight>
* Fichier '''VehiculeRoulant.cpp'''
<
...
Ligne 751 :
...
</syntaxhighlight>
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:
<
void Automobile::avancer()
{
Ligne 762 :
position -= frottementRoues;
}
</syntaxhighlight>
=== 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:
<
class C : ''type_héritage'' A, ''type_héritage'' B ''...''
{
......
};
</syntaxhighlight>
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.
<
class Base
{
Ligne 833 :
}
};
</syntaxhighlight>
=== Voir aussi ===
Ligne 843 :
=== Syntaxe ===
<
Une telle méthode définie uniquement dans une sous-classe est nécessairement virtuelle. Le mot-clé <code>virtual</code> est donc optionnel.
<
type nom_méthode(paramètres); // Méthode d'une sous classe plus abstraite</
=== Exemple ===
<
class Animal
{
Ligne 857 :
virtual cstring cri() = 0;
}
</syntaxhighlight>
== Pointeur de membre ==
Ligne 867 :
=== Exemple ===
<
int CBox::* pBoxInt;
// pBoxInt pointe un entier dans les objets de la classe CBox
</syntaxhighlight>
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 ===
<
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>
== Voir aussi ==
|