Fabien LE LEZ wrote on 25/06/2006 02:37:
On 24 Jun 2006 21:48:06 +0200, Jean-Marc Bourguet <jm@bourguet.org>:
static std::string const ma_chaine ("://");
const implique static si il n'y a pas une déclaration
antérieure qui indique que c'est avec un "linkage" externe.
Même si le "static" est de toutes façons implicite, ne devrait-on pas
le mettre explicitement ?
De même, dans le code struct A { virtual void f(); }
struct B { (virtual) void f(); }
le "virtual" est implicite ; doit-on tout de même l'écrire
explicitement ?
oups, peut être était-ce vraiment l'écriture intentionelle, se traduisant par "si je n'indique pas 'virtual' ai-je tout de même les attributs d'une déclaration (explicitement) virtuelle" ?
je ne vais pas me contenter d'un 'non' car cela a évidemment un impact sur le design des classes et qu'il est utile d'avoir en tête ces conséquences; si ce n'est pas ta question, cela servira peut être à d'autres...
petit détour: en Java, une méthode d'instance est implicitement virtuelle. ceci implique que:
- une résolution virtuelle est tjrs effectuée lors de l'appel d'une méthode d'instance,
- un client ne peut bypasser la hiérarchie fournie par l'instance (quand il invoque la méthode f() de l'instance c'est la méthode surchargée qui est appelée.
à contrario en C++, la virtualité n'est pas implicite et doit être déclarée par la classe qui introduit une méthode et souhaite la rendre virtuelle, notons pour ne pas y revenir qu'il n'est pas possible de rendre virtuele une méthode surchargée d'une classe parent qui n'était pas originellmeent virtuelle.
cette non virtualité systématique implique que par défaut la surcharge ne masque pas une méthode - le client d'une instance ayant surchargé des méthodes de ses parents a accès aux méthodes de la classe comme à celles du (ou des) parents (on omets ici les restriction de visibilité explicite par protected et private, l'instance est considérée comme une structure publique pour simplifier)
ainsi si:
struct A { void f(){} };
struct B : A { void f(){} };
alors ayant:
A a; B b;
a.f(); // est valide, bien sur et également
b.f(); // qui invoque B::f() avec le contexte de b, comme
(A& b).f(); // qui appelle A::f() avec le contexte de b
de plus la résolution n'étant pas "dynamisé" par une déclaration virtuelle; un pointeur A* même initialisé avec une instance de B ne verra que les méthodes de A:
A* pa = &B;
pa->f(); // invoque A::f() et non B::f().
(on vent arrêter là les détails verbieux)
si par contre nous déclarons la méthode virtuelle:
struct A { virtual void f(){} };
struct B : A { void f(){} };
alors la surcharge est maintenant masquante pour les clients de la classe:
b.f(); // invoque B::f()
(A& b).f(); // invoque également B::f()
bien B a accès aux détails de A, on peut avoir:
void B::f(){ A::f(); } // appel à la 'super-méthode'
autre impact, la résolution d'appel est dynamique donc:
A* pa = &B;
pa->f(); // invoque encore B::f().
la 1ière remarque (masquage) peut être vue comme une conséquence de ce second point, ou on peut voir ce second point comme le moyen d'apporter le premier (qui serait alors le but .. de l'encapsulation), le résultat est le même (la motivation et ses arcanes sont très différentes).
voila.
Sylvain.