Chapitre fonction membres, merci Hassan Azi
This commit is contained in:
parent
602c9d2402
commit
44c7b43e47
|
@ -0,0 +1,165 @@
|
|||
[set
|
||||
title = "Fonctions membres"
|
||||
partAs = correction
|
||||
translator = "Raphaël Jakse"
|
||||
]
|
||||
|
||||
# Des valeurs intermédiaires potentiellement négatives rfinent [c decrement()] légèrement plus compliquéée que [c increment()]~ :
|
||||
|
||||
[code=d <<<
|
||||
struct MomentDuJour
|
||||
{
|
||||
// ...
|
||||
|
||||
void decrement(in Duree duree)
|
||||
{
|
||||
auto minutesARetrancher = duree.minute % 60;
|
||||
auto heuresARetrancher = duree.minute / 60;
|
||||
|
||||
minute -= minutesARetrancher;
|
||||
|
||||
if (minute < 0) {
|
||||
minute += 60;
|
||||
++heuresARetrancher;
|
||||
}
|
||||
|
||||
heure -= heuresARetrancher;
|
||||
|
||||
if (heure < 0) {
|
||||
heure = 24 - (-heure % 24);
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
>>>]
|
||||
|
||||
Pour voir à quel point c'est plus facile avec des fonction membres [c toString()], jetons une fois de plus un œil à la surcharge d'[c info()] correspondant à la structure [c Reunion]~ :
|
||||
|
||||
[code=d <<<
|
||||
void info(in Reunion reunion)
|
||||
{
|
||||
info(reunion.debut);
|
||||
write('-');
|
||||
info(reunion.fin);
|
||||
|
||||
writef(" Réunion \"%s\" avec %s participants",
|
||||
reunion.sujet,
|
||||
reunion.nombreParticipants);
|
||||
}
|
||||
>>>]
|
||||
|
||||
En nous servant de la fonction [c MomentDuJour.toString] déjà définie, l'implémentation de [c Reunion.toString] devient triviale~ :
|
||||
|
||||
[code=d <<<
|
||||
string toString()
|
||||
{
|
||||
return format("%s-%s Réunion \"%s\" avec %s participants",
|
||||
debut, fin, sujet, nombreParticipants);
|
||||
}
|
||||
>>>]
|
||||
|
||||
Voici le programme entier~ :
|
||||
|
||||
[code=d <<<
|
||||
import std.stdio;
|
||||
import std.string;
|
||||
|
||||
struct Duree
|
||||
{
|
||||
int minute;
|
||||
}
|
||||
|
||||
struct MomentDuJour
|
||||
{
|
||||
int heure;
|
||||
int minute;
|
||||
|
||||
string toString()
|
||||
{
|
||||
return format("%02s:%02s", heure, minute);
|
||||
}
|
||||
|
||||
void increment(in Duree duree)
|
||||
{
|
||||
minute += duree.minute;
|
||||
|
||||
heure += minute / 60;
|
||||
minute %= 60;
|
||||
heure %= 24;
|
||||
}
|
||||
}
|
||||
|
||||
struct Reunion
|
||||
{
|
||||
string sujet;
|
||||
int nombreParticipants;
|
||||
MomentDuJour debut;
|
||||
MomentDuJour fin;
|
||||
|
||||
string toString()
|
||||
{
|
||||
return format("%s-%s Réunion \"%s\" with %s attfinees",
|
||||
debut, fin, sujet, nombreParticipants);
|
||||
}
|
||||
}
|
||||
|
||||
struct Repas
|
||||
{
|
||||
MomentDuJour moment;
|
||||
string adresse;
|
||||
|
||||
string toString()
|
||||
{
|
||||
MomentDuJour fin = moment;
|
||||
fin.increment(Duree(90));
|
||||
|
||||
return format("%s-%s Repas, Adresse: %s",
|
||||
moment, fin, adresse);
|
||||
}
|
||||
}
|
||||
|
||||
struct PlanningJournalier
|
||||
{
|
||||
Reunion amReunion;
|
||||
Repas repas;
|
||||
Reunion pmReunion;
|
||||
|
||||
string toString()
|
||||
{
|
||||
return format("%s\n%s\n%s",
|
||||
amReunion,
|
||||
repas,
|
||||
pmReunion);
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
auto ReunionVelo = Reunion("Vélo", 4,
|
||||
MomentDuJour(10, 30),
|
||||
MomentDuJour(11, 45));
|
||||
|
||||
auto repas = Repas(MomentDuJour(12, 30), "İstanbul");
|
||||
|
||||
auto reunionBudget = Reunion("Budget", 8,
|
||||
MomentDuJour(15, 30),
|
||||
MomentDuJour(17, 30));
|
||||
|
||||
auto emploiDuTempsAujourdhui = PlanningJournalier(ReunionVelo,
|
||||
repas,
|
||||
reunionBudget);
|
||||
|
||||
writeln(emploiDuTempsAujourdhui);
|
||||
writeln();
|
||||
}
|
||||
>>>]
|
||||
|
||||
La sortie du programme est la même que la version précédente qui utilisait les surcharges de la fonction [c info()]~ :
|
||||
|
||||
[output <<<
|
||||
10:30-11:45 Réunion "Vélo" avec 4 participants
|
||||
12:30-14:00 Repas, Adresse : İstanbul
|
||||
15:30-17:30 Réunion "Budget" avec 8 participants
|
||||
>>>]
|
||||
|
|
@ -1,6 +1,362 @@
|
|||
[set
|
||||
title = "Fonction membres"
|
||||
partAs = chapitre
|
||||
translator = Hassan Azi
|
||||
]
|
||||
|
||||
[fixme En attente d'écriture.]
|
||||
Bien que ce chapitre se concentre principalement sur les structures, une grande partie des informations détaillées ci-après peuvent être également appliquées aux classes également.
|
||||
|
||||
Au cours de ce chapitre, nous couvrirons les méthodes des structures, notamment la méthode spéciale [c toString()] qui est utilisée afin de représenter les objets sous forme d'une chaîne de caractères, ou [c string].
|
||||
|
||||
Lorsqu'une structure ou une classe est définie, on définit aussi souvent un certain nombre de fonctions avec elle. Nous avions déjà vu de exemples dans le chapitres précédents: [c ajouterDuree()] et la surcharge de [c info()] ont été écrites spécialement pour être utilisées avec le type [c TempsDuJour]. En quelque sorte, ces deux fonctions forment l'[i interface] de [c TempsDuJour].
|
||||
|
||||
[c ajouterDuree()] et [c info()] avaient pris pour premier paramètre un objet [c TempsDuJour] sur lequel elles allaient opérer. En plus de cela, comme c'est le cas pour toutes les fonctions jusque-là étudiées, nos deux fonctions avaient été définies au [i niveau du module] (''module level''), en dehors de toute autre portée.
|
||||
|
||||
Le fait qu'un ensemble de fonctions définisse l'interface d'une structure est un concept très répandu. Pour cette raison, les fonctions ayant une relation proche à un certain type peuvent être définies au sein de celui-ci.
|
||||
|
||||
[ = Définir des méthodes
|
||||
|
||||
Les fonctions définies à l'intérieur des accolades d'une [c struct] sont dites des [i méthodes]~ :
|
||||
|
||||
[code=d <<<
|
||||
struct UneStructure
|
||||
{
|
||||
void methode(/* les paramètres de la fonction */)
|
||||
{
|
||||
// ... la définition de la fonction ...
|
||||
}
|
||||
|
||||
// ... les autres membres de la structure ...
|
||||
}
|
||||
>>>]
|
||||
|
||||
Les méthodes peuvent être accédées de la même façon que les champs, en les séparant du nom de l'objet par un point:
|
||||
|
||||
[code=d <<<
|
||||
[i objet.methode(arguments));
|
||||
>>>]
|
||||
|
||||
Nous avions déjà utilisé les méthodes quand on avait pris soin de spécifier [c stdin] and [c stdout] lors des opérations d'entrée et de sortie~ :
|
||||
|
||||
|
||||
[code=d <<<
|
||||
stdin.readf(" %s", &nombre);
|
||||
stdout.writeln(nombre);
|
||||
>>>]
|
||||
|
||||
Dans ce code, nous avons effectué des appels aux méthodes [c readf()] et [c writeln()] qui opèrent respectivement sur les objets [c stdin] et [c stdout].
|
||||
|
||||
|
||||
Définissions maintenant [c info()] en tant que méthode. Auparavant, nous l'avions définie ainsi~ :
|
||||
|
||||
|
||||
[code=d <<<
|
||||
void info(in TempsDuJour temps)
|
||||
{
|
||||
writef("%02s:%02s", temps.heure, temps.minute);
|
||||
}
|
||||
>>>]
|
||||
|
||||
Afin de transformer [c info()] en une méthode, il faudra non seulement déplacer sa définition à l'intérieur de la structure, mais aussi lui faire subir deux modifications~ :
|
||||
|
||||
[code=d <<<
|
||||
struct TempsDuJour
|
||||
{
|
||||
int heure;
|
||||
int minute;
|
||||
|
||||
void info() // (1)
|
||||
{
|
||||
writef("%02s:%02s", heure, minute); // (2)
|
||||
}
|
||||
}
|
||||
>>>]
|
||||
|
||||
# La méthode ne prend plus l'objet comme paramètre.
|
||||
# Suite à ceci, ele accède aux champs directement par [c heure] et [c minute].
|
||||
|
||||
La raison pour cela est que les méthodes sont toujours appelées sur un objet existant. Ce dernier est implicitement disponible pour la méthode~ :
|
||||
|
||||
[code=d <<<
|
||||
auto temps = TempsDuJour(10, 30);
|
||||
temps.info();
|
||||
>>>]
|
||||
|
||||
La méthode [c info()] est appelée sur l'objet [c temps] ci-avant. Les variables [c heure] et [c minute] qui sont indiquées à l'intérieur de la définition de la fonction correspondent aux champs de l'objet [c temps], plus précisément, [c temps.heure] et [c temps.minute].
|
||||
|
||||
Ici, l'appel à la méthode est presque l'équivalent de l'appel à une fonction normale~ :
|
||||
|
||||
[code=d <<<
|
||||
temps.info(); // méthode
|
||||
info(temps); // fonction habituelle (la définition précédente)
|
||||
>>>]
|
||||
|
||||
Dès qu'une méthode est appelée sur un objet, les champs de celui-ci sont implicitement accessibles pour la fonction~ :
|
||||
|
||||
[code=d <<<
|
||||
auto matin = TempsDuJour(10, 0);
|
||||
auto apresmidi = TempsDuJour(22, 0);
|
||||
|
||||
matin.info();
|
||||
write('-');
|
||||
apresmidi.info();
|
||||
writeln();
|
||||
>>>]
|
||||
|
||||
Appelée sur [c matin], les variables [c heure] et [c minute] utilisées à l'intérieur de la méthode ne sont autre que [c matin.heure] et [c matin.minute]. De même, en appelant [c apresmidi.info()], elles font référence à [c apresmidi.heure] et [c apresmidi.minute]~ :
|
||||
|
||||
[output <<<
|
||||
10:00-22:00
|
||||
>>>]
|
||||
|
||||
[ = [c toString()] pour les représentations sous formes de chaînes de caractères
|
||||
|
||||
Dans le chapitre précédent, nous venons de parler des limitations de la fonction [c info()]. Mais malgré tout ce que nous venons de voir, la fonction garde au moins un autre inconvénient: Bien qu'elle affiche le temps dans un format lisible, le programmeur devra toujours prendre soin d'expliciter le caractère [c '-'] ainsi que le caractère de fin de ligne.
|
||||
|
||||
Il serait plus pratique si les objets [c TempsDuJour] pouvaient être utilisés comme des types fondamentaux dans le code ci-après~ :
|
||||
|
||||
[code=d <<<
|
||||
writefln("%s-%s", matin, apresmidi);
|
||||
>>>]
|
||||
|
||||
En plus d'avoir réduit quatre lignes de code en seulement une, cela aurait aussi pour effet de pouvoir écrire l'objet à n'importe quel flux~ :
|
||||
|
||||
[code=d <<<
|
||||
auto fichier = File("temps_information", "w");
|
||||
fichier.writefln("%s-%s", matin, apresmidi);
|
||||
>>>]
|
||||
|
||||
La méthode [c toString()] des types définis par l'utilisateur est un peu spéciale : elle est automatiquement appelée pour produire la représentation [c string] des objets. [c toString()] doit impérativement retourner la représentation [c string] de l'objet en question.
|
||||
|
||||
Avant de plonger dans les détails, étudions d'abord la définition de la fonction [c toString()]~ :
|
||||
|
||||
[code=d <<<
|
||||
import std.stdio;
|
||||
|
||||
struct TempsDuJour
|
||||
{
|
||||
int heure;
|
||||
int minute;
|
||||
|
||||
string toString()
|
||||
{
|
||||
return "a_faire";
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
auto matin = TempsDuJour(10, 0);
|
||||
auto apresmidi = TempsDuJour(22, 0);
|
||||
|
||||
writefln("%s-%s", matin, apresmidi);
|
||||
}
|
||||
>>>]
|
||||
|
||||
Pour le moment, [c toString()] ne produit pas grand chose, mais l'exécution du code ci-avant montre qu'elle a effectivement été appelée à deux reprises par [c writefln()]~ :
|
||||
|
||||
[output <<<
|
||||
a_faire-a_faire
|
||||
>>>]
|
||||
|
||||
Remarquez aussi que [c info()] est désormais redondante vu que [c toString()] offre la même fonctionnalité.
|
||||
|
||||
La façon la plus simple d'implémenter [c toString()] serait d'appeler la fonction [c format()], celle-ci est disponible dans le module [c std.string]. Le fonctionnement de [c format()] est similaire à celui des fonctions d'affichage formaté comme [c writef()]. La seule différence réside dans le fait qu'au lieu d'afficher directement les variables, [c format()] retourne le résultat sous la forme d'une [c string].
|
||||
|
||||
|
||||
[c toString()] peut donc directement retourner le résultat de [c format()]~ :
|
||||
|
||||
[code=d <<<
|
||||
import std.string;
|
||||
// ...
|
||||
struct TempsDuJour
|
||||
{
|
||||
// ...
|
||||
string toString()
|
||||
{
|
||||
return format("%02s:%02s", heure, minute);
|
||||
}
|
||||
}
|
||||
>>>]
|
||||
|
||||
Remarquez que [c toString()] retourne uniquement la représentation textuelle de l'objet [i this]. [c writefln()] se charge d'afficher le reste: elle appelle la méthode [c toString()] pour chacun des deux objets tout en prenant soin d'afficher le caractère [c '-'] entre les deux appels, puis termine la ligne~ :
|
||||
|
||||
[output <<<
|
||||
10:00-22:00
|
||||
>>>]
|
||||
|
||||
La définition de [c toString()] qui a été expliquée ci-avant ne prend aucun paramètre; elle produit simplement une [c string] et la retourne. Cependant, il existe une autre façon de définir [c toString()], celle-ci prend en paramètre un [c delegate]. Nous étudierons cette définition plus tard dans le chapitre sur les pointeurs de fonctions, les delegates et les lambdas.
|
||||
]
|
||||
[ = Exemple: la méthode [c increment())]
|
||||
|
||||
Définissons une méthode qui ajoute une certaine durée aux objets [c TempsDuJour].
|
||||
|
||||
Avant d'aller plus loin, corrigeons d'abord un défaut de conception qui était là tout le temps. Dans le chapitre sur des structures, nous avions vu que l'addition de deux objets [c TempsDuJour] dans [c ajouterDuree] n'avait pas de sens~ :
|
||||
|
||||
[code=d <<<
|
||||
TempsDuJour ajouterDuree(in TempsDuJour start,
|
||||
in TempsDuJour duree) // absurde
|
||||
{
|
||||
// ...
|
||||
}
|
||||
>>>]
|
||||
|
||||
Il serait plus naturel d'ajouter une [i durée] à un point dans le temps. En ajoutant par exemple la durée d'un voyage à son temps de départ, on obtiendrait le temps d'arrivée.
|
||||
|
||||
D'un autre côté, soustraire deux points dans le temps est une opération naturelle, et dans ce cas le résultat serait une [i durée].
|
||||
|
||||
Le programme ci-après définit une structure [c Duree] d'une précision allant à la minute, ainsi qu'une fonction [c ajouterDuree()] qui l'utilise:
|
||||
|
||||
|
||||
[code=d <<<
|
||||
struct Duree
|
||||
{
|
||||
int minute;
|
||||
}
|
||||
|
||||
TempsDuJour ajouterDuree(in TempsDuJour debut,
|
||||
in Duree duree)
|
||||
{
|
||||
// Commençons par faire une copie de start
|
||||
TempsDuJour resultat = debut;
|
||||
|
||||
// Ajoutons-y la durée
|
||||
resultat.minute += duree.minute;
|
||||
|
||||
// Puis prenons soin de régler les débordements
|
||||
resultat.heure += resultat.minute / 60;
|
||||
resultat.minute %= 60;
|
||||
resultat.heure %= 24;
|
||||
|
||||
return resultat;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
// Un test banal
|
||||
assert(ajouterDuree(TempsDuJour(10, 30), Duree(10))
|
||||
== TempsDuJour(10, 40));
|
||||
|
||||
// Un temps à minuit
|
||||
assert(ajouterDuree(TempsDuJour(23, 9), Duree(51))
|
||||
== TempsDuJour(0, 0));
|
||||
|
||||
// Un temps dans le jour suivant
|
||||
assert(ajouterDuree(TempsDuJour(17, 45), Duree(8 * 60))
|
||||
== TempsDuJour(1, 45));
|
||||
}
|
||||
>>>]
|
||||
|
||||
Maintenant, redéfinissons cette fonction sous la forme d'une méthode. Jusqu'à présent, [c ajouterDuree()] retournait un nouvel objet à chaque appel. Définissons donc une méthode [c incrementer()] qui modifiera directement l'objet [i this]~ :
|
||||
|
||||
[code=d <<<
|
||||
struct Duree
|
||||
{
|
||||
int minute;
|
||||
}
|
||||
|
||||
struct TempsDuJour
|
||||
{
|
||||
int heure;
|
||||
int minute;
|
||||
|
||||
string toString()
|
||||
{
|
||||
return format("%02s:%02s", heure, minute);
|
||||
}
|
||||
|
||||
void $(HILITE incrementer)(in Duree duree)
|
||||
{
|
||||
minute += duree.minute;
|
||||
|
||||
heure += minute / 60;
|
||||
minute %= 60;
|
||||
heure %= 24;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
auto temps = TempsDuJour(10, 30);
|
||||
|
||||
// Un test banal
|
||||
temps$(HILITE .incrementer)(Duree(10));
|
||||
assert(temps == TempsDuJour(10, 40));
|
||||
|
||||
// 15 heures plus tard devra être dans le jour suivant
|
||||
temps$(HILITE .incrementer)(Duree(15 * 60));
|
||||
assert(temps == TempsDuJour(1, 40));
|
||||
|
||||
// 22 heures et 20 minutes devra être à minuit
|
||||
temps$(HILITE .incrementer)(Duree(22 * 60 + 20));
|
||||
assert(temps == TempsDuJour(0, 0));
|
||||
}
|
||||
}
|
||||
>>>]
|
||||
|
||||
[c incrementer()] incrémente la valeur de l'objet dépendamment de la durée qu'on lui passe. Plus tard, nous étudierons comment la surcharge d'opérateur ([i operator overloading]) va nous permettre d'ajouter une durée à l'aide de l'opérateur [c +=]~ :
|
||||
|
||||
[code=d <<<
|
||||
temps += Duree(10); // sera expliqué dans un chapitre ultérieur
|
||||
>>>]
|
||||
|
||||
Notez également que les blocs de tests unitaires [c unittest] peuvent être écrits à l'intérieur des définitions des [c struct] aussi, c'est surtout le cas lorsque l'on souhaite tester ses méthodes. Mais il reste tout de même possible de déplacer de tels blocs [c unittest] en dehors du corps de la structure:
|
||||
|
||||
[code=d <<<
|
||||
struct TempsDuJour
|
||||
{
|
||||
// ... définition de la structure ...
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
// ... tests de la structure ...
|
||||
}
|
||||
>>>]
|
||||
]
|
||||
]
|
||||
|
||||
[ = Exercices
|
||||
|
||||
# Ajoutez une méthode [c decrementer()] à [c TempsDuJour] qui en retrancherait la durée de temps spécifiée. Tout comme pour [c incrementer()], elle devrait repasser au jour précédent s'il n'y a pas assez de temps dans le jour actuel. Par exemple, soustraire 10 minutes de 00:05 devra donner pour résultat 23:55.
|
||||
|
||||
En d'autres termes, implémentez [c decrementer()] de façon à ne pas échouer les tests unitaires suivant~ :
|
||||
|
||||
[code=d <<<
|
||||
struct TempsDuJour
|
||||
{
|
||||
// ...
|
||||
|
||||
void decrementer(in Duree duree)
|
||||
{
|
||||
// ... veuillez implémenter cette fonction ...
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
auto temps = TempsDuJour(10, 30);
|
||||
|
||||
// Un test banal
|
||||
temps.decrementer(Duree(12));
|
||||
assert(temps == TempsDuJour(10, 18));
|
||||
|
||||
// 3 jours and 11 heures plus tôt
|
||||
temps.decrementer(Duree(3 * 24 * 60 + 11 * 60));
|
||||
assert(temps == TempsDuJour(23, 18));
|
||||
|
||||
// 23 heures and 18 minutes plus tôt devra être à minuit
|
||||
temps.decrementer(Duree(23 * 60 + 18));
|
||||
assert(temps == TempsDuJour(0, 0));
|
||||
|
||||
// 1 minute plus tôt
|
||||
temps.decrementer(Duree(1));
|
||||
assert(temps == TempsDuJour(23, 59));
|
||||
}
|
||||
}
|
||||
>>>]
|
||||
|
||||
# Convertissez également les surcharges de [c infos()] en méthodes [c toString()] des structures [c Reunion], [c Repas] et [c MomentDeLaJournee]. (Consulter [[doc:corrections/surcharge_fonctions | les solutions du chapitre sur les surcharges de fonctions]] pour voir comment les différentes implémentations de [c info()]).
|
||||
|
||||
Vous remarquerez qu'en plus de rendre leurs structures respectives plus pratiques, les implémentations des méthodes [c toString()] ne prendront chacune qu'une ligne de code.
|
||||
|
||||
[[part:corrections/fonctions_membres | … Les solutions]]
|
||||
]
|
||||
|
|
|
@ -26,7 +26,9 @@
|
|||
|
||||
Merci à sclytrac (forum D) pour ses corrections.
|
||||
|
||||
Merci à Stéphane Goujet pour ses relectures attentives et pointilleuses qui ont permise d'élever la qualité et la cohérence de la traduction.
|
||||
Merci à Hassan Azi pour sa traduction du chapitre sur les fonctions membres.
|
||||
|
||||
Merci à Olivier Pisano pour une traduction d'excellente qualité de quelques chapitres.
|
||||
|
||||
Merci à Stéphane Goujet pour ses relectures attentives et pointilleuses qui ont permise d'élever la qualité et la cohérence de la traduction.
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue