correction de l'indentation dans classes.whata

This commit is contained in:
Raphaël Jakse 2016-02-28 00:13:17 +01:00
parent d2821a620f
commit 68a77c80d2
1 changed files with 46 additions and 35 deletions

View File

@ -13,7 +13,7 @@ L'encapsulation est obtenue par des ''attributs de protection'', que nous verron
Ce chapitre présente sommairement les classes, en soulignant le fait que ce sont des types références. Les classes seront expliquées plus en détail dans des chapitres ultérieurs.
[ = Comparaison avec les structures
[ = Comparaison avec les structures
De manière générale, les classes sont très similaires aux structures. La plupart des fonctionnalités que nous avons vues dans les chapitres suivants s'appliquent également aux classes :
- structures
@ -26,7 +26,7 @@ Il y a néanmoins des différences importantes entres les classes et les structu
[ = Les classes sont des type références
La principale différence avec les structures est que ces dernières sont des types valeurs et que les classes sont des types références. Les autres différences résumées ci-après sont essentiellement dues à ce fait.
La principale différence avec les structures est que ces dernières sont des types valeurs et que les classes sont des types références. Les autres différences résumées ci-après sont essentiellement dues à ce fait.
]
[ = Les variables classes peuvent être nulles
@ -39,12 +39,12 @@ Comme indiqué brièvement dans le chapitre sur la valeur ``null`` et l'opérate
MaClasse variable; // ne référence pas un objet
assert(variable is null);
>>>]
>>>]
La raison en est la suivante : l'opérateur ``==`` peut avoir besoin de consulter les valeurs des membres des objets et tenter d'accéder aux membres d'une variable ``null`` causerait une erreur d'accès mémoire. Pour cette raison, les variables classes doivent toujours être comparées au moyen des opérateurs ``is`` et ``!is``.
]
[ = Les variables classe et les objets classes
[ = Les variables classe et les objets classes
Les variables classes et les objets classes sont des concepts séparés.
Les objets classes sont construits via le mot clé ``new`` ; ils n'ont pas de nom. Le véritable concept représenté par un type classe dans un programme est fourni par un objet classe. Par exemple, en supposant une classe ``Etudiant`` qui représente des étudiants avec leurs noms et leurs notes, on pourrait enregistrer ces informations dans les membres des objets ``Etudiant``. En partie parce qu'ils sont anonymes, il n'est pas possible d'accéder à des objets classes directement.
@ -54,7 +54,7 @@ Considérons le code suivant que nous avions vu précédemment dans le chapitre
[code=d <<<
auto variable1 = new MaClasse;
auto variable2 = variable1;
>>>]
>>>]
Le mot-clé ``new`` construit un objet calles anonyme. Les variables ``variable1`` et ``variable2`` ci-dessus fournissent juste un accès à cet objet anonyme :
@ -66,7 +66,7 @@ Le mot-clé ``new`` construit un objet calles anonyme. Les variables ``variable1
▲ │ │
│ │ │
└────────────────────┴────────────┘
>>>]
>>>]
]
[ = Copie
@ -78,7 +78,7 @@ Puisqu'aucun objet n'est copié, la fonction postblit ``this(this)`` n'est pas d
[code=d <<<
auto variable2 = variable1;
>>>]
>>>]
Dans le code ce-dessus, ``variable2`` est initialisée au moyen de ``variable1``. Les deux variables fournissent un accès au même objet.
Lorsque l'objet réel doit être copié, la classe doit fournir une fonction membre à cet effet. Afin d'être compatible avec les tableaux, on peut nommer cette fonction ``dup()``. Cette fonction doit créer et retourner un nouvel objet classe. Voyons cela sur une classe qui a plusieurs types de membres :
@ -100,7 +100,8 @@ Lorsque l'objet réel doit être copié, la classe doit fournir une fonction mem
Foo dup() const {
return new Foo(o, s, i);
}
} >>>]
}
>>>]
La fonction membre ``dup()`` fabrique un nouvel objet via le constructeur de ``Foo`` et retourne ce nouvel objet. Notez que le constructeur copie le membre s explicitement via la propriété ``.dup`` des tableaux. Étant des types valeurs, ``o`` et ``i`` sont copiés automatiquement.
Le code suivant utilise ``dup()`` pour créer un nouvel objet :
@ -108,12 +109,12 @@ Le code suivant utilise ``dup()`` pour créer un nouvel objet :
[code=d <<<
auto var1 = new Foo(S(1.5), "bonjour", 42);
auto var2 = var1.dup();
>>>]
>>>]
En résultat, les objets qui sont associés à ``var1`` et ``var2`` sont distincts.
Pareillement, une copie immutable d'un objet peut être fournie par une fonction membre appropriée appelée ``idup()`` :
[code=d <<<
[code=d <<<
class Foo {
// ...
immutable(Foo) idup() const {
@ -123,16 +124,17 @@ Pareillement, une copie immutable d'un objet peut être fournie par une fonction
// ...
immutable(Foo) imm = var1.idup(); >>>]
immutable(Foo) imm = var1.idup();
>>>]
]
[ = Affectation
[ = Affectation
Tout comme la copie, l'affectation modifie seulement les variables.
Affecter à une variable classe dissocie cette variable de son objet courant et l'associe à un nouvel objet.
Si aucune autre variable classe ne fournit d'accès à l'objet qui vient d'être dissocié de la variable, alors cet objet sera détruit plus tard par le ramasse-miettes.
[code=d <<<
[code=d <<<
auto variable1 = new MaClasse();
auto variable2 = new MaClasse();
variable1 = variable2; >>>]
@ -146,29 +148,31 @@ Le comportement de l'affectation ne peut pas être changé pour les classes. En
Les classes sont définies au moyen du mot-clé ``class`` en lieu et place du mot-clé ``struct`` :
[code=d <<<
[code=d <<<
class PieceEchec {
// ...
} >>>]
}
>>>]
]
[ = Construction
Comme pour les structures, le nom du constructeur est ``this``. Contrairement aux structures, les objets classes ne peuvent être construits avec la syntaxe { }.
[code=d <<<
[code=d <<<
class PieceEchec {
dchar forme;
this(dchar forme) {
this.forme = forme;
}
} >>>]
}
>>>]
Contrairement aux structures, il n'y a pas de construction d'objet automatique lorsque les paramètres du constructeurs sont affectés aux membres séquentiellement :
[code=d <<<
[code=d <<<
class PieceEchec {
dchar forme;
size_t valeur;
@ -176,25 +180,28 @@ Contrairement aux structures, il n'y a pas de construction d'objet automatique l
void main() {
auto roi = new PieceEchec('♔', 100); // ← ERREUR de compilation
} >>>]
}
>>>]
[code <<<
Erreur: pas de constructeur pour PieceEchec >>>]
[code <<<
Erreur: pas de constructeur pour PieceEchec
>>>]
Pour que cette syntaxe fonctionne, un constructeur doit être défini explicitement par le programmeur.
]
[ = Destruction
[ = Destruction
Comme pour les structures, le nom du destructeur est ``~this`` :
[code=d <<<
~this() {
// ...
} >>>]
}
>>>]
Néanmoins, à la différence des structures, les destructeurs des classes ne sont pas exécutés au moment où la durée de vie d'un objet classe se termine. Comme nous l'avons vu plus avant, le destructeur est appelé plus tard pendant un cycle du ramasse-miettes. (Par cette distinction, les destructeurs de classe auraient du être appelés plus précisément finaliseurs).
Néanmoins, à la différence des structures, les destructeurs des classes ne sont pas exécutés au moment où la durée de vie d'un objet classe se termine. Comme nous l'avons vu plus avant, le destructeur est appelé plus tard pendant un cycle du ramasse-miettes. (Par cette distinction, les destructeurs de classe auraient du être appelés plus précisément finaliseurs).
Comme nous le verrons plus tard dans le chapitre sur la gestion de la mémoire, les destructeurs de classe doivent respecter les règles suivantes :
- un destructeur de classe ne peut pas accéder à une membre qui est géré par le ramasse-miettes. La raison en est que les ramasse-miettes ne sont pas tenus de garantir l'ordre dans lequel l'objet et ses membres sont finalisés lorsque le ramasse-miettes s'exécute.
- Un destructeur de classe ne doit pas allouer de mémoire qui soit gérée par le ramasse-miette. En effet, les ramasses-miettes ne sont pas tenus de garantir qu'ils puissent allouer de nouveaux objets durant un cycle de ramasse-miettes.
@ -204,14 +211,15 @@ Enfreindre ces règles est un comportement indéfini. Il est facile de voir un e
[code=d <<<
class C {
~this() {
auto c = new C(); // ← FAUX : allocation explicite dans un
auto c = new C(); // ← FAUX : allocation explicite dans un
// destructeur de classe
}
}
void main() {
auto c = new C();
} >>>]
}
>>>]
Le programme est interrompu au moyen d'une exception :
@ -222,19 +230,21 @@ Il est également faux de provoquer une allocation mémoire du ramasse-miettes i
[code=d <<<
~this() {
auto tabl = [ 1 ]; // ← FAUX : Allocation indirecte dans un
auto tabl = [ 1 ]; // ← FAUX : Allocation indirecte dans un
// destructeur de classe
} >>>]
}
>>>]
]
[ = Accès aux membres
[ = Accès aux membres
Comme avec les structures, on accède aux membres au moyen de l'opérateur point :
[code=d <<<
auto roi = new PieceEchec('♔');
writeln(roi.forme); >>>]
writeln(roi.forme);
>>>]
Même si la syntaxe peut faire croire que l'on accède à un membre de la variable, c'est en fait un membre de l'objet. Les variables classes n'ont pas de membres, ce sont les objets membres qui en ont. La variable ``roi`` n'a pas de membre forme, c'est l'objet anonyme qui en a.
''Note : Il n'est normalement pas correct d'accéder directement aux membres comme dans le code ci-avant. Quand cette même syntaxe est désirée, les propriétés devraient être préférées. Elles seront abordées dans un chapitre ultérieur.''
@ -245,7 +255,7 @@ Même si la syntaxe peut faire croire que l'on accède à un membre de la variab
À part le fait qu'``opAssign`` ne puisse pas être surchargé pour les classes, la surcharge d'opérateur est identique aux structures. Pour les classes, la signification de ``opAssign`` est toujours associée à une variable classe avec un objet classe.
]
[ = Fonctions membres
[ = Fonctions membres
Bien que les fonctions membres soient définies et utilisées de la même manière que les structures, il existe une différence importante : les fonction membres des classes peuvent être spécialisées par défaut. Nous verrons ce concept plus tard dans le chapitre sur l'héritage.
Puisque les fonctions membres spécialisables ont un impact sur les performances à l'exécution, sans entrer plus dans les détails, je vous recommande de définir toutes les fonctions membres des classes qui n'ont pas besoin d'être spécialisables avec le mot-clé ``final``. Vous pouvez appliquer cette consigne aveuglément sauf en cas d'erreurs de compilation :
@ -254,7 +264,8 @@ Puisque les fonctions membres spécialisables ont un impact sur les performances
final int fonct() { // ← Recommandé
// ...
}
} >>>]
}
>>>]
Une autre différence par rapport aux structures est que certaines fonctions membres sont automatiquement héritées de la classe ``Object``. Nous verrons dans le chapitre suivant comment la définition de ``toString`` peut être changée par le mot-clé override.
]
@ -268,7 +279,7 @@ L'opérateur ``is`` indique si deux variables classes fournissent un accès au m
auto monRoi = new PieceEchec('♔');
auto tonRoi = new PieceEchec('♔');
assert(monRoi !is tonRoi);
>>>]
>>>]
Puisque les objets des variables ``monRoi`` et ``tonRoi`` sont différents, l'opérateur ``!is`` retourne ``true``. Même si les deux objets sont construits avec le même caractère '♔' il sont néanmoins distincts.
Lorsque les variables fournissent un accès au même objet, ``is`` retourne ``true`` :
@ -276,7 +287,7 @@ Lorsque les variables fournissent un accès au même objet, ``is`` retourne ``tr
[code=d <<<
auto monRoi2 = monRoi;
assert(monRoi2 is monRoi);
>>>]
>>>]
Les deux variables ci-avant fournissent un accès au même objet.