Après avoir lu Les questions de la série constructrice de Ian Boyd ( 1 , 2 a >, 3 , 4 ), je me rends compte que je ne fais pas t assez saisir la signification littérale sur ce qui est caché.
Je sais (corrigez-moi si je me trompe) La seule usine code> Le seul but est de pouvoir avoir un comportement polymorphique, de sorte que Le temps peut résoudre une méthode en fonction du type réel d'une instance - par opposition au type déclaré. Considérez le code suivant: P>
type
TBase = class
procedure Proc1; virtual;
procedure Proc2; virtual;
end;
TChild = class(TBase)
procedure Proc1; override;
procedure Proc2; // <- [DCC Warning]
end;
procedure TBase.Proc1;
begin
Writeln('Base.Proc1');
end;
procedure TBase.Proc2;
begin
Writeln('Base.Proc2');
end;
procedure TChild.Proc1;
begin
inherited Proc1;
Writeln('Child.Proc1');
end;
procedure TChild.Proc2;
begin
inherited Proc2;
Writeln('Child.Proc2');
end;
var
Base: TBase;
begin
Base := TChild.Create;
Base.Proc1;
Writeln;
Base.Proc2;
Base.Free;
Readln;
end.
3 Réponses :
Parmi parmi d'autres choses, vous ne pourrez pas définir
Base.Proc2 Base.Proc2 Child.Proc2
C'est faux. Supprimez le remplacement, créez une instance 'Tagandandchild' à partir d'une «TBASE» et appelez «proc2» dessus. L'appel serait résolu à "tbase.proc2".
Dans le commentaire précédent, je voulais écrire "la directive de dérogation" au lieu de "le remplacement". Ne ferait pas la différence sur le résultat même ..
@Sertatac - Ken est correct. La chose que l'avertissement vous avertit est le Inabilité i> à remplacer tbase.proc2 des descendants de Tchild. Le code Ken a écrit ne sera pas compilé, car Tchild.Proc2 n'est pas virtuel. Évidemment, il était destiné à tobase.proc2 d'être remplacé, mais il ne peut pas être remplacé, car il a été caché par Tchild.Proc2.
Merci @barry. À partir de maintenant, chaque fois que je vois l'avertissement, je le lirai: "" méthode "% s" la virtualité de la méthode de type de base "% s" des classes descendantes " i> Si ça va.
@Ken - Je suppose que j'ai eu un problème avec le sens que j'ai fait de la phrase d'avertissement. En fait, je n'avais jamais cependant des classes de descendants possibles, merci pour la vue. Néanmoins, j'apprécierais également une explication sur ce que l'on entend par "appels hérités faisant référence à la procédure initiale" i>. Quand ne le font-ils pas?
@Sertatac il ne "cache pas la virtualité". Tchild.proc2 est une méthode différente pour tbase.proc2. Ce n'est pas la virtualité qui est cachée; C'est une méthode entière.
@Barry - Désolé d'être une tête épaisse, je crois comprendre que les descendants ne seraient pas en mesure de remplacer la méthode de base, c'est pourquoi je pensais (seulement la «virtualité» était terminée. Pouvez-vous dire la signification pratique d'être une méthode différente (autre que de pouvoir remplacer les descendants)?
@Sertac - une chose que vous devrez peut-être comprendre est que les messages d'avertissement supposent que vous avez nommé TCHILD.PROC2 de la même manière que TBASE.PROC2 "par accident". C'est la façon dont le compilateur vous dit "hey! Il y a déjà une méthode nommée de cette façon!". S'il est "par conception" que vous masquez la méthode, vous devez le signaler avec "réintroduire" pour supprimer l'avertissement. Quant à la signification pratique, vous l'avez déjà montré dans votre poste initial. Je modifierai ma réponse pour ajouter un exemple plus significatif.
@Ken - Je connais la raison de l'avertissement et je connais la conséquence. Je sais aussi réintroduire. Ce que je ne sais pas, c'est ce que "tbase.proc2" est caché signifie. Dans votre code et mon code, c'est en fait celui qui fonctionne lorsque "TCHILD" ne remplace pas "proc2". Quand Barry a dit (it) "est l'incapacité de remplacer tbase.proc2 des descendants de TCHILD" I> Je pensais avoir compris. Mais quand il dit "" tchild.proc2 "est une méthode tout à fait différente qui cache" tbase.proc2 " i> je le perds à nouveau. 'Tbase.proc2' est là, je peux l'appeler, 'tchild.proc2' est une méthode tout à fait différente. Comment est-ce que "tbase.proc2" caché?
Vous ne pouvez pas appeler tbase.Proc2 à partir d'une référence d'objet de type TCHILD. La seule façon de le faire est de le faire en taper. (I.e. TBASE (ENFANT) .PROC2). Calling Enfant.Proc2 appellerait le Tchild.Proc2. Appeler "hérité" de Tchild.Proc2 et ne remplace pas la méthode est une mauvaise programmation imo, à moins que vous ne souhaitiez enfreindre le polymorphisme. Donc, à partir d'une référence d'objet TCHILD, tbase.proc2 est cachée. (Vous ne pouvez pas l'appeler par nom). Il a toujours une entrée dans la VMT de l'objet, mais vous ne pouvez appeler que la méthode statique TCHILD.PROC2.
@Ken -> Ce que je faisais appelé était l'appel hérité - bien sûr dans l'exemple que j'ai posté le polymorphisme brisé est intentionnel, pourquoi omettrait-il autrement? .. Outre, comment appelerait "tbase.proc2" serait différent si 'TCHILD' l'a annulé?
Appeler tbase (enfant) .Proc2 appellerait toujours tchild.proc2 s'il était remplacé. En ce qui concerne l'avertissement ... L'avertissement est là pour les personnes qui nomment par erreur une procédure dans laquelle un ancêtre l'a déjà nommé. Si le polymorphisme brisant est intentionnel, vous devez utiliser "réintroduire".
@Ken - Signification, c'est le même appel, soit c'est remplacé par la nervation ou non. .. Si j'avais utilisé «réintroduire» comment puis-je poser des questions sur l'avertissement du compilateur? ;)
Si vous remplacez, vous appelez une méthode virtuelle. Donc, le compilateur générera du code pour appeler la routine GetDynamethod pour trouver l'adresse de la bonne méthode à appeler. Si vous ne le faites pas, le compilateur connaît déjà l'adresse de la bonne méthode à appeler et appelez simplement cela, car la méthode est statique. Donc, non, l'appel n'est pas la même chose.
@Ken - Oh, je voulais dire «même appel» du code de l'utilisateur. Je ne pense pas que les "peaux" dans l'avertissement feraient référence aux détails de la mise en œuvre du compilateur. Il y a quelque chose que le code d'utilisateur ne peut pas faire s'il ne remplace pas la méthode de base (autre que de terminer la chaîne virtuelle et le polymorphisme de la chaîne virtuelle), et je ne comprends pas ce que c'est.
@SertaC - Oui, les descendants ne peuvent pas remplacer la méthode car elle a été cachée. Lorsque vous faites FOO.METHOD, le compilateur lève la méthode sur le type statique de FOO et ses ancêtres, jusqu'à ce qu'il trouve une définition. Au moment de l'exécution, il l'appelle; Si c'est virtuel, cela finira par appeler la définition la plus dérivée (remplacement) de cette méthode. Cacher la méthode de base (A) empêche les ancêtres de remplacer la méthode, et (B) les empêche d'être la définition la plus dérivée (remplacement) de cette méthode. C'est parce qu'il est caché. Mais je peux vous voir clairement pas veux I> de comprendre, je vais arrêter de vous informer.
Je pense que vous devriez aller lire un peu sur le polymorphisme pour comprendre comment cela fonctionne. En ce qui concerne la réponse, déclarer un Tgrandchild avec une fonction PROC non remplacée ne cachera pas TCHILD.PROC de TBASE.PROC. Tgrandchild.Proc est totalement indépendant pour TCHILD.PROC et TBASE.PROC à côté du partage du nom.
@Ken - Merci pour la réponse, je ne savais pas que vous avez répondu à mon commentaire lorsque je l'ai supprimé, désolé pour cela.
Vous pensez trop compliqué. La cachette ne signifie pas que vous perdez complètement l'accès à l'original. Cela signifie simplement (et vous avez déjà noté cela vous-même) si vous avez un objet de type statique TCHILD et que vous appelez ProC2 dessus, cela appelle celui de TCHILD, et non celui de TBASE. Aussi ce que Ken a dit est vrai. P>
Cela vous avertit parce que l'original est virtuel et que la cachette n'est probablement pas ce que les gens ont l'intention de rédiger un code comme celui-ci. À tout le moins c'est un mauvais style de codage. P>
Merci Timo, en effet, je pensais que cachant la perte d'accès à la méthode de la classe de base ... Je ne suis pas ce que vous voulez dire avec votre prochaine déclaration. Appeler 'proc2' sur un "enfant" déclaré comme un "Tchild", serait bien sûr "Tchild.Proc2". Est-ce pertinent avec "dérober" / "cacher"?
Sry, foiré avec mon dernier commentaire. En général, je définirais la cachette comme ceci: vous avez un identifiant dans une certaine portée et vous déclarez cet identifiant pour signifier autre chose. Par exemple, vous pouvez avoir une variable globale X, puis vous déclarez une variable locale X qui cache le global. C'est semblable ici. Considérons un code comme "enfant.proc2;" où l'enfant est TCHILD. Si proc2 n'est pas redéclamé à TCHild, il appelle TBASE.PROC2. Mais parce qu'il est redéclaré, il appelle Tchild.Proc2.
Utilisez 'réintroduire' pour supprimer l'avertissement. P>
Bienvenue dans le débordement de pile. Votre empressement de participer est inspirant. Mais la prochaine fois, n'oubliez pas de lire la question avant de répondre. Sertac n'a pas demandé comment se débarrasser de l'avertissement.
Pourquoi réintroduit-il code> supprime l'avertissement?
@Ian parce que parfois c'est le comportement souhaité. Le but entier de réintroduit code> est de supprimer l'avertissement;
réintroduite code> ne fait rien d'autre.