OVH Cloud OVH Cloud

Virtualité

22 réponses
Avatar
Seb
Bonjour,

Je me suis toujours posé la question suivante : pourquoi quand une méthode
est virtuelle, le mot "virtual" est optionnel lors de sa surcharge ?
Personnellement, je trouve que le mettre dans le code est à la fois clair et
pas difficile.

Sébastien

10 réponses

1 2 3
Avatar
Seb
"Philippe Guglielmetti" a écrit dans le message
news: 3f96507c$0$3677$
"Seb" a écrit >
Je l'ai testé avec le compilateur Borland C++ 5.5. Le résultat est :
ABCAAA, puis ABCAAC


VC6 et VC7 donnent ABCAAC dans les deux cas , ce qui signifie que la
méthode de C herite de l'attribut "virtual", et sauf erreur c'est le
comportement prévu par le standard.
Ce qui m'étonne plus, c'est que B puisse définir une méthode du même
nom en static, je savais pas que c'était permis, et à mon avis très
ambigu...


Alors là je suis perdu : Dans mon cas ou j'obtiens ABCAAA, je me disais que
le static void f() générait "une autre méthode de même nom et de même
signature" (si tant est que cela signifie quelque chose) et que donc lorsque
je fais

x = &c;
x->f();

J'appel f virtuel car le f de B est une "nouvelle fonction" et C ne fait que
surcharger celle de B (compliqué mais ça semblait tenir la route).

Sébastien


Avatar
Christophe Lephay
Seb wrote:
"Christophe Lephay" a écrit dans le
Je pense qu'il y a des cas où le fait de rajouter virtual est utile,
notemment si la classe dont tu hérites (héritant elle même de la
classe de base déclarant les fonctions virtuelles) définit une
nouvelle interface (en rajoutant son lot de nouvelles fonctions
virtuelles, voire en en déplaçant certaines en private), mais je
pense que ce cas n'est pas suffisemment général pour imposer la
répétition du virtual dans toutes les classes dérivées...
class A {

public:
virtual void f() { cout << "Fonction de A" << endl; }
};

class B : public A
{
public:
static void f() { cout << "Fonction de B" << endl; }
};

class C : public B
{
public:
void f() { cout << "Fonction de C" << endl; }
};


Sans polémiquer sur le fait de redéfinir f() en static, dans l'exemple que
tu donnes, B redéfinit l'interface de A et est donc un bon candidat à mes
yeux pour ré-écrire le virtual. En fait de changement d'interface, je
pensais plus à un truc du style :

class A
{
protected:
virtual void f();
virtual void g();
};

class B : public A
{
// f() passe en private
void f();

protected:
// je ré-écris le virtual
virtual void g();

// on rajoute une fonction virtuelle
virtual void h();
};

Si j'écris une classe C héritant de B, j'ai vraiment besoin de lire la
déclaration de B dans la mesure où B définit une nouvelle interface. Dans le
cas contraire, la lecture de A suffirait, rendant inutile la ré-écriture du
virtual dans B...

Chris


Avatar
Philippe Guglielmetti
"Seb" a écrit:
Alors là je suis perdu : Dans mon cas ou j'obtiens ABCAAA, je me disais
que

le static void f() générait "une autre méthode de même nom et de même
signature" (si tant est que cela signifie quelque chose) et que donc
lorsque

je fais

x = &c;
x->f();

J'appel f virtuel car le f de B est une "nouvelle fonction" et C ne fait
que

surcharger celle de B (compliqué mais ça semblait tenir la route).


oui, ça tient la route si on considère qu'avec BC++5.1 le C::f surcharge le
B::f, ce qui fait que tous les accès via le pointeur aboutissent sur A::f
(résultat AAA), alors qu'avec VC6et7, le C::f surcharge la méthode virtuelle
A::f, ce qui fait que l'accès par les pointeurs aboutit bien sur AAC. Je ne
sais pas quel comportement est le "bon"...

Essaie
1) d'enlever le "static" sur ton BC++ pour voir : sur VC6/7 on obtient alors
ABCABC ce qui serait à mon sens le comportement espéré.
2) d'enlever ensuite le "virtual" de la méthode A::f. Ici j'ai ABCAAA ce qui
est aussi logique
3) d'ajouter ensuite "virtual" à la méthode B::f. Ici j'ai encore ABCAAA et
ça me scie parce que j'étais persuadé que ça faisait une erreur de compil de
définir une fonction virtual de même prototype qu'une fonction héritée pas
virtuelle...

--
Philippe Guglielmetti - www.dynabits.com

Avatar
Seb
"Philippe Guglielmetti" a écrit dans le message
news: 3f966fd5$0$3670$
"Seb" a écrit:
Alors là je suis perdu : Dans mon cas ou j'obtiens ABCAAA, je me
disais que le static void f() générait "une autre méthode de même
nom et de même signature" (si tant est que cela signifie quelque
chose) et que donc lorsque je fais

x = &c;
x->f();

J'appel f virtuel car le f de B est une "nouvelle fonction" et C ne
fait que surcharger celle de B (compliqué mais ça semblait tenir la
route).


oui, ça tient la route si on considère qu'avec BC++5.1 le C::f
surcharge le B::f, ce qui fait que tous les accès via le pointeur
aboutissent sur A::f (résultat AAA), alors qu'avec VC6et7, le C::f
surcharge la méthode virtuelle A::f, ce qui fait que l'accès par les
pointeurs aboutit bien sur AAC. Je ne sais pas quel comportement est
le "bon"...

Essaie
1) d'enlever le "static" sur ton BC++ pour voir : sur VC6/7 on
obtient alors ABCABC ce qui serait à mon sens le comportement espéré.
2) d'enlever ensuite le "virtual" de la méthode A::f. Ici j'ai ABCAAA
ce qui est aussi logique
3) d'ajouter ensuite "virtual" à la méthode B::f. Ici j'ai encore
ABCAAA et ça me scie parce que j'étais persuadé que ça faisait une
erreur de compil de définir une fonction virtual de même prototype
qu'une fonction héritée pas virtuelle...


Avant de faire ton test : j'ai utilisé le compilateur Comeau en ligne et
j'obtiens

"ComeauTest.c", line 14: error: only nonstatic member functions may be
virtual
static void f() { cout << "Méthode B"<< endl; }
^

1 error detected in the compilation of "ComeauTest.c".


Ce qui paraît être normal.


Avatar
Seb
"Philippe Guglielmetti" a écrit dans le message
news: 3f966fd5$0$3670$
"Seb" a écrit:
Alors là je suis perdu : Dans mon cas ou j'obtiens ABCAAA, je me
disais que le static void f() générait "une autre méthode de même
nom et de même signature" (si tant est que cela signifie quelque
chose) et que donc lorsque je fais

x = &c;
x->f();

J'appel f virtuel car le f de B est une "nouvelle fonction" et C ne
fait que surcharger celle de B (compliqué mais ça semblait tenir la
route).


oui, ça tient la route si on considère qu'avec BC++5.1 le C::f
surcharge le B::f, ce qui fait que tous les accès via le pointeur
aboutissent sur A::f (résultat AAA), alors qu'avec VC6et7, le C::f
surcharge la méthode virtuelle A::f, ce qui fait que l'accès par les
pointeurs aboutit bien sur AAC. Je ne sais pas quel comportement est
le "bon"...

Essaie
1) d'enlever le "static" sur ton BC++ pour voir : sur VC6/7 on
obtient alors ABCABC ce qui serait à mon sens le comportement espéré.
2) d'enlever ensuite le "virtual" de la méthode A::f. Ici j'ai ABCAAA
ce qui est aussi logique
3) d'ajouter ensuite "virtual" à la méthode B::f. Ici j'ai encore
ABCAAA et ça me scie parce que j'étais persuadé que ça faisait une
erreur de compil de définir une fonction virtual de même prototype
qu'une fonction héritée pas virtuelle...


J'ai les mêmes résultats pour les 3 tests.

J'aimerais vraiment savoir ce que la norme dit à ce sujet ...

Sébastien


Avatar
Philippe Guglielmetti
J'aimerais vraiment savoir ce que la norme dit à ce sujet ...


Moi aussi, alors j'ai posté la question sur comp.lang.c++,
copie ci-dessous pour ne pas heurter les toubonistes...
--
Philippe Guglielmetti - www.dynabits.com

Look at these few lines of code:

class A { public: virtual void f() { cout << "A";}};
class B : public A{public: static void f() { cout << "B"; }};
class C : public B{public: void f() { cout << "C"; }}; // virtual or not ?
that's the question...

A* x; A a; B b; C c;
a.f(); b.f(); c.f();
x = &a; x->f(); x = &b; x->f(); x = &c; x->f();

Borland C++ 5.5 outputs "ABCAAA"
MS VC6 and 7 give "ABCAAC"

my explanation is that C::f overrides the virtual A::f with VC,
while C::f overrides the static B::f with BC5. In fact, if C is redefined as

class C : public B{public: virtual void f() { cout << "C"; }};

then BC5 behaves as VC

Questions:
1) what does the standard say? (I was unable to find a clear answer..)
2) what is the reason for allowing the above code to compile without even a
warning ?

Avatar
Jean-Marc Bourguet
"Philippe Guglielmetti" writes:

J'aimerais vraiment savoir ce que la norme dit à ce sujet ...


Moi aussi, alors j'ai posté la question sur comp.lang.c++,
copie ci-dessous pour ne pas heurter les toubonistes...
--
Philippe Guglielmetti - www.dynabits.com

Look at these few lines of code:

class A { public: virtual void f() { cout << "A";}};
class B : public A{public: static void f() { cout << "B"; }};
class C : public B{public: void f() { cout << "C"; }}; // virtual or not ?
that's the question...

A* x; A a; B b; C c;
a.f(); b.f(); c.f();
x = &a; x->f(); x = &b; x->f(); x = &c; x->f();
Borland C++ 5.5 outputs "ABCAAA"
MS VC6 and 7 give "ABCAAC"


gcc, sun CC, como et hp aCC n'acceptent pas le code.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Loïc Joly
Philippe Guglielmetti wrote:


J'aimerais vraiment savoir ce que la norme dit à ce sujet ...



Moi aussi, alors j'ai posté la question sur comp.lang.c++,
copie ci-dessous pour ne pas heurter les toubonistes...


D'après ma compréhension de la norme, le code en question n'est pas valide.

9.4.1 Static member functions
[...] A static member function shall not be virtual. There shall not be
a static and a nonstatic member function with the same name and the same
parameter types (13.1).

--
Loïc


Avatar
Seb
"Loïc Joly" a écrit dans le message news:
bn6g9j$gij$
Philippe Guglielmetti wrote:


J'aimerais vraiment savoir ce que la norme dit à ce sujet ...



Moi aussi, alors j'ai posté la question sur comp.lang.c++,
copie ci-dessous pour ne pas heurter les toubonistes...


D'après ma compréhension de la norme, le code en question n'est pas
valide.


9.4.1 Static member functions
[...] A static member function shall not be virtual. There shall not be
a static and a nonstatic member function with the same name and the same
parameter types (13.1).

--
Loïc



Effectivement : Le code ne devrait pas être valide ...

Sébastien



Avatar
Philippe Guglielmetti
d'après les réponses sur comp.lang.c++:
1) beaucoup de compilateurs autres que VC et BC refusent le code
2)
9.4.1 Static member functions
[...] A static member function shall not be virtual. There shall not be
a static and a nonstatic member function with the same name and the same
parameter types (13.1).
n'est pas clair, car les méthodes non statiques ont un paramètre (this)


caché.
On peut donc soit interpréter ce paragraphe comme :
class A {static f() {}; f() {}; }; // est interdit
ou
class A {static f(A*) {}; f() {}; }; // est interdit

3) tout le monde est d'accord pour dire que BC5.5 ne respecte pas la règle
de surcharge des fonctions : dans l'exemple de Seb, C::f doit être virtuelle
car elle surcharge A::f (elle a un paramètre this...) et non B::f (qui n'en
a pas...).
4) "surcharge" ("override") n'est pas le terme exact, il faut dire "cache"
("hide") dans le cas de méthodes non virtuelles.
--
Philippe Guglielmetti - www.dynabits.com
"Seb" a écrit dans le message de
news:bn7uui$ses$

"Loïc Joly" a écrit dans le message news:
bn6g9j$gij$
Philippe Guglielmetti wrote:


J'aimerais vraiment savoir ce que la norme dit à ce sujet ...



Moi aussi, alors j'ai posté la question sur comp.lang.c++,
copie ci-dessous pour ne pas heurter les toubonistes...


D'après ma compréhension de la norme, le code en question n'est pas
valide.


9.4.1 Static member functions
[...] A static member function shall not be virtual. There shall not be
a static and a nonstatic member function with the same name and the same
parameter types (13.1).

--
Loïc



Effectivement : Le code ne devrait pas être valide ...

Sébastien







1 2 3