Complètement HS :
- en français, on met un espace avant et après les signes de ponctuation double (: ; ? etc) et seulement après pour les signes de ponctuation simple (. , etc).
- Pas de majuscule avec :
Pourquoi des majuscule en plein milieu de certaines phrases ? (effet de style ?)
- C++ et pas c++
Je te conseille de passer ton texte dans un correcteur grammatical, par exemple bonpatron.com
Sur le fond :
Bien que je suppose que vous savez déjà ce qu'est l'héritage, je vous propose un petit rappel:
En fait, cette partie représente la moitié de l'article, ce n'est donc pas un petit rappel. (mais ce n'est pas une mauvaise chose, au contraire)
Lors de la création de class, vous utilisez souvent (voir même toujours) le mot clé public pour les méthodes et le mot clé private pour les attributs et quelques méthodes sensibles afin de respecter l'encapsulation.
L'encapsulation n'est pas simplement mettre les variables en privée et ajouter des accesseurs. Ne pas donner cette impression au lecture.
std::string getCouleur(){}
Si tu ne donnes que la déclaration, ne mets pas le bloc. Si tu veux donner la définition, mets le bloc complet (ou au moins 3 points pour indiquer que tu n'as pas mit le code).
De plus, pour un getter, mettre les const et référence :
| const std::string & getCouleur() const;
// voire
constexpr std::string & getCouleur() const noexcept;
|
Voyons ce que l'héritage apporte lors de l'utilisation de pointeur:
C'est plutôt les pointeurs/références qui "apportent" quelque chose à l'héritage (le polymorphisme justement - on peut faire de l'héritage sans pointeur/référence, mais pas de polymorphisme dans ce cas).
Parler de type dynamique et type statique ?
Pourquoi des pointeurs et pas des références ? Pourquoi pas des pointeurs intelligents ?
Cela est dû au LSP:
Non. Le LSP ne justifie pas cette erreur de compilation (je dirais que c'est tout simplement la norme C++ qui interdit cela)
Le LSP est une règle que doit s'imposer les développeurs pour utiliser "correctement" l'héritage public (ie avoir un code qui soit le plus évolutif, maintenable, etc. possible)
Lorsque l'on déclare la même méthode à la fois dans une class fille que dans une class mère, le programme peut ne pas se comporter de la façon voulue si notre objet instancié à partir de la class fille est utilisé via un pointeur de type class mère.
HS : manque des "e" à tes classes.
Pour commencer, non. Ce n'est pas un problème de masquage. Si on écrit pas la fonction dans la classe enfant (donc pas de masquage) et que l'on appelle la fonction sur le type statique parent, cela plante :
| class A {};
class B : A {
void f();
};
A a;
a.f(); // erreur
A* a = new B;
a->f(); // erreur
|
Le masquage est lorsque l'on définie une fonction dans une classe enfant avec le même nom qu'une fonction de la classe parent, la fonction de la classe parente n'est plus accessible (sauf si on utilise using). Dans certain cas, le masquage est ce que l'on veut obtenir. Par exemple :
| struct A {
constexpr string & static_type() const noexcept { return "A"; }
};
struct B : A {
constexpr string & static_type() const noexcept { return "B"; }
};
|
(Totalement inutile dans ce cas, mais osef…)
L'autre chose, c'est que ce n'est pas cette forme de masquage qui pose problème, c'est le masquage avec des fonctions qui porte le même nom, mais des paramètres différents :
| struct A {
void f(int);
};
struct B : A {
void f(string); // masque f(int)
};
B b;
b.f(1); // erreur, f(int) est masqué
|
Dans l'exemple que tu donnes, Fraise::presentation masque bien Fruit::presentation, mais si le code suivant n'affiche pas la même chose, ce n'est pas à cause du masquage, mais du fait que les types statique sont différents (et donc la fonction appelée est différente).
(HS : quand on est dans une hiérarchie de classes, avoir plusieurs fonctions avec le même nom produit un masquage. Avec des fonctions libres, on a une surcharge - overload (corrige moi Luc si je me trompe encore sur les termes), les 2 fonctions sont accessibles en même temps et le compilateur choisit celle qui correspond aux arguments d'appel de la fonction).
Et oui , Le soucis ici: La résolution statique des liens. Lors de la compilation, le compilateur sait que fraise est une fraise. mais dans le cas du pointeur , il attends un Fruit et non une Fraise, ainsi , lors de la compilation, impossible de savoir si on pointera sur un Fruit ou une Fraise… du coup le compilateur prévoit l'utilisation de la méthode de la class mère. D'ailleurs, si vous crée une nouvelle méthode dans la Class Fraise et que vous essayé de l'appeler via le pointeur, sa ne compilera tout simplement pas car elle n'existera pas dans la class mère. Il y a une astuce que je vous donnerai plus bas.
Confus je trouve.
A l'aide du polymorphisme , nous allons aider l'ordinateur à retrouver la bonne méthode à appeler lorsque que l'on passe une fille dans un ptr de sa Class mère.
Je trouve que "aider le compilateur" est maladroit. Ce n'est pas qu'il n'arrive pas à "trouver la bonne méthode", cela peut être un comportement voulu. C'est une nouvelle "fonctionnalité", qui permet d'appeler une fonction en fonction du type dynamique et pas du type statique. (donc fonction non virtuelle + pas de pointeur/référence = résolution de l'appel des fonctions selon le type statique ; fonction virtuelle + pointeur/référence = résolution de l'appel des fonctions selon le type dynamique).
Et pas se focaliser sur les pointeurs, le polymorphisme marche aussi avec les références.
Voila, ce petit mot magique viens de vous sauver la vie!
J'aime pas la magie en programmation. En particulier parce que je comprends rien à la magie et que pas comprendre quelque chose en programmation me pose problème…
Mise en garde : Le virtual ne se situe QUE dans le Header. donc lorsque vous virtualisé , ce sont que les prototype qui possède le mot clé , pas l'implémentation , capiche?! sinon je mords!
HS : j'aime pas le ton "je parle à mes copains" dans un article, c'est une des choses que je déteste dans les tutos du SdZ.
header = fichier d'en-tête
"vous virtualisé" : il y a une faute d'accord et cela veut rien dire.
Remarque : pour éviter le masquage d'une fonction virtuelle, penser aux mots-clés override et final
si on essayait de mettre un cout dans les destructeurs pour voir ce qu'il s'y passe?
HS : je le fais aussi, mais dans l'absolue, cout n'est pas noexcept, donc ne devrait pas être appelé dans un destructeur (sauf erreur de ma part… mais je chipote)
le code
Problème d'indentation
Pour palier à ce problème, la même solution que plus haut: virtualiser le destructeur.
Peut être rappeler la règle : "public et virtuel ou privé et non virtuel" pour les classes qui peuvent être dérivée (sinon, interdiction de dériver)
Les collections hétérogènes
Aussi les références (avec std::reference_wrapper)
Le design pattern Visitor, c'est pas mal avec les collections hétérogènes… (et cela t'évitera de trop parler du dynamic_cast)
std::string getCouleur(){} // La, ICI! Vous voyez pas?
J'ai l'impression finalement que c'était volontaire de donner une implémentation vide… ce qui n'est pas une bonne idée. Un compilateur correctement configuré te fera la remarque que ta fonction devrait retourner quelque chose.
Ceci étant mon premier tuto écrit, merci de ne pas me fouetter en place public si cela ne vous est pas compréhensible.
Note finale pas nécessaire, le tuto devra être compréhensible lorsqu'il sera publié. Sinon, cela ne sert pas à grand chose
En tout cas, bonne initiative comme tuto
(HS : désolé pour la longueur. On t'avait pas prévenu que dès que l'on parle de C++, il y a quelques casse-pieds qui relisent tout en détail ? )