Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Pointeur sur fonction membre virtuelle

7 réponses
Avatar
Loïc Joly
Bonjour,

Soit le cas suivant :

struct A
{
virtual void f();
};

struct B : A
{
virtual void f();
}

typedef void (A::*FctPtr)();

FctPtr fct = &A::f;

int main()
{
A *b = new B;
b->*fct();
}

(désolé s'il y a des fautes de frappes, j'ai pas compilé le code, c'est
l'idée qui compte)

Dans ce cas, quelle fonction est appelée : A::f ou B::f ?

J'ai cherché dans la norme sans rien trouver de bien convainquant.
Le 10.3.12
Explicit qualification with the scope operator (5.1) suppresses the
virtual call mechanism.

Me fait penser que comme on a explicité A::f, il n'y aura pas appel
virtuel, mais j'avoue ne pas savoir si ce passage est suffisant ou même
approprié.

Merci,

--
Loïc

7 réponses

Avatar
Lahsen
Trace la fonction f dans A et dans B et puis appelle la par un pointeur
et tu sera.

Bonjour,

Soit le cas suivant :

struct A
{
virtual void f();
};


void A::f()
{
cout << " f de A est appelé" << endl;
}

struct B : A
{
virtual void f();
}


void B::f()
{
cout << " f de B est appelé" << endl;
}

typedef void (A::*FctPtr)();

FctPtr fct = &A::f;

int main()
{
A *b = new B;
b->*fct();
}


C'est quoi *fct()??? Il faut peut être la déclarer avant de l'appeler!!!

(désolé s'il y a des fautes de frappes, j'ai pas compilé le code, c'est
l'idée qui compte)

Dans ce cas, quelle fonction est appelée : A::f ou B::f ?

J'ai cherché dans la norme sans rien trouver de bien convainquant.
Le 10.3.12
Explicit qualification with the scope operator (5.1) suppresses the
virtual call mechanism.

Me fait penser que comme on a explicité A::f, il n'y aura pas appel
virtuel, mais j'avoue ne pas savoir si ce passage est suffisant ou même
approprié.

Merci,



Avatar
Jean-Marc Bourguet
Loïc Joly writes:

Bonjour,

Soit le cas suivant :

struct A
{
virtual void f();
};

struct B : A
{
virtual void f();
}


;

typedef void (A::*FctPtr)();

FctPtr fct = &A::f;

int main()
{
A *b = new B;
b->*fct();
(b->*fct)();

}

(désolé s'il y a des fautes de frappes, j'ai pas compilé le code, c'est
l'idée qui compte)

Dans ce cas, quelle fonction est appelée : A::f ou B::f ?


B::f -- d'après mes souvenirs et mes compilateurs -- mais j'ai trouvé de
référence.

J'ai cherché dans la norme sans rien trouver de bien convainquant. Le
10.3.12 Explicit qualification with the scope operator (5.1) suppresses
the virtual call mechanism.

Me fait penser que comme on a explicité A::f, il n'y aura pas appel
virtuel, mais j'avoue ne pas savoir si ce passage est suffisant ou même
approprié.


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
Jean-Marc Bourguet
Lahsen writes:

Trace la fonction f dans A et dans B et puis appelle la par un pointeur
et tu sera.


Loïc cherche peut-être quelque chose de plus sûr que le comportement de son
compilateur (et puis il a écrit qu'il n'en avait pas sous la main)

FctPtr fct = &A::f;

b->*fct();



(b->*fct)();

C'est quoi *fct()??? Il faut peut être la déclarer avant de l'appeler!!!


J'ai laissé sa définition et corrigé son utilisation.

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
Michel Decima

typedef void (A::*FctPtr)();

FctPtr fct = &A::f;

int main()
{
A *b = new B;
b->*fct();
}


C'est quoi *fct()???


C'est l'appel de la fonction via un pointeur sur fonction membre.
D'ailleurs mon g++ me dit qu'il faut des parentheses supplementaires,
comme ceci: (b->*fct)();

Il faut peut être la déclarer avant de l'appeler!!!


C'est fait sur la ligne qui precede le main()


Avatar
James Kanze
Loïc Joly wrote:

Soit le cas suivant :

struct A
{
virtual void f();
};

struct B : A
{
virtual void f();
}

typedef void (A::*FctPtr)();

FctPtr fct = &A::f;

int main()
{
A *b = new B;
b->*fct();


Il y manque des parenthèses : ce que tu a écris est
l'équivalent de « b->*(fct()) », c-à-d qu'il appellerait une
fonction libre fct, qui doit renvoyer un pointeur à membre, et
puis accéderait au membre désigné (pour ne rien faire avec lui)
de l'objet désigné par b.

Essaie « (b->*fct)() ».

}

(désolé s'il y a des fautes de frappes, j'ai pas compilé le
code, c'est l'idée qui compte)

Dans ce cas, quelle fonction est appelée : A::f ou B::f ?


B::f ?

J'ai cherché dans la norme sans rien trouver de bien convainquant.
Le 10.3.12
Explicit qualification with the scope operator (5.1) suppresses the
virtual call mechanism.


§10.3/13 (au moins dans la version de travail le plus récente)
ne parle que vaguement des appels directs. Où il faut chercher,
c'est dans la section 5, sur la signification des expressions.
Surtout §5.2.2/1 (vers la fin) : « The function called in a
member function call is normally selected according to the
static type of the object expression (clause 10), but if that
function is virtual and is not specified using a qualified id
then the function actually called will be the final overrider
(10.3) of the selected function in the dynamic type of the
object expression ». Or, ici, la fonction est spécifiée au
moyen d'un pointeur à membre, qui n'est pas une « qualified
id ». Donc, si la fonction est virtuelle, la fonction réelement
appelée serait la supplantation finale de la fonction choisie
dans le type dynamique de l'expression designant l'objet.

Si tu as l'occasion de régarder dans l'ARM, Stroustrup y propose
une implémentation possible, qui correspond d'assez près à celle
utiliser dans CFront (et encore dans VC++, je crois) : un
pointeur à une function membre est en fait une structure, qui
contient l'information si la fonction est virtuelle ou non, et
le cas échéant, soi son adresse réele, soi l'indice de son
entrée dans le vtable. (L'autre implémentation que je connais,
c'est de générer un trampoline, c-à-d une fonction sans nom qui
ressemble à une fonction membre non-virtuelle de A, et qui
appelle la fonction en question, et de mettre l'adresse du
trampoline dans le pointeur. C'est celui qui sert aujourd'hui
dans g++ et dans Sun CC.)

Me fait penser que comme on a explicité A::f, il n'y aura pas
appel virtuel, mais j'avoue ne pas savoir si ce passage est
suffisant ou même approprié.


La suppression de la virtualité dépend de la syntaxe de l'appel.
Si la fonction est désignée par une expression qualifiée,
l'appel n'est jamais virtuelle. Sinon, l'appel est virtuel si la
fonction appelée est déclarée virtuelle.

En passant, c'est intéressant à noter qu'avec des pointeurs au
membre, on a même une forme assez restreinte de la virtualité
pour les données :

struct A {} ;
struct B : A { int i ; } ;

int A::*pi = static_cast< int A::* >( &B::i ) ;

void
f( A* p )
{
std::cout << p->*pi ;
}

C'est légal, et ça marche, dans la mesure où l'objet désigné par
p a réelement un type dynamique B (ou quelque chose qui dérive
de B -- et je crois qu'il y a des restrictions dans le cas des
dérivations virtuelles).

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
Loïc Joly
Merci à tous de vos réponses.


Il y manque des parenthèses : ce que tu a écris est
l'équivalent de « b->*(fct()) », c-à-d qu'il appellerait une
fonction libre fct, qui doit renvoyer un pointeur à membre, et
puis accéderait au membre désigné (pour ne rien faire avec lui)
de l'objet désigné par b.

Essaie « (b->*fct)() ».


Effectivement. C'est typiquement le genre du code où j'ai tendance à
beaucoup compter sur le compilateur pour corriger mes lacunes syntaxiques.

J'ai cherché dans la norme sans rien trouver de bien convainquant.
Le 10.3.12
Explicit qualification with the scope operator (5.1) suppresses the
virtual call mechanism.



§10.3/13 (au moins dans la version de travail le plus récente)
ne parle que vaguement des appels directs. Où il faut chercher,
c'est dans la section 5, sur la signification des expressions.
Surtout §5.2.2/1 (vers la fin) : « The function called in a
member function call is normally selected according to the
static type of the object expression (clause 10), but if that
function is virtual and is not specified using a qualified id
then the function actually called will be the final overrider
(10.3) of the selected function in the dynamic type of the
object expression ». Or, ici, la fonction est spécifiée au
moyen d'un pointeur à membre, qui n'est pas une « qualified
id ».


D'accord. Ce qui m'a induit en erreur, c'est que ce pointeur avait lui
été crée à partir d'un « qualified id», mais visiblement, c'est
uniquement au moment même de l'appel si ce dernier est utilisé que la
résolution n'est pas virtuelle.

Si tu as l'occasion de régarder dans l'ARM, Stroustrup y propose
une implémentation possible, qui correspond d'assez près à celle
utiliser dans CFront (et encore dans VC++, je crois) : un
pointeur à une function membre est en fait une structure, qui
contient l'information si la fonction est virtuelle ou non, et
le cas échéant, soi son adresse réele, soi l'indice de son
entrée dans le vtable. (L'autre implémentation que je connais,
c'est de générer un trampoline, c-à-d une fonction sans nom qui
ressemble à une fonction membre non-virtuelle de A, et qui
appelle la fonction en question, et de mettre l'adresse du
trampoline dans le pointeur. C'est celui qui sert aujourd'hui
dans g++ et dans Sun CC.)


C'est en fait vers une version manuelle de ce deuxième idiome que je me
serais dirigé si la réponse avait été l'inverse de ce qu'elle est.

En passant, c'est intéressant à noter qu'avec des pointeurs au
membre, on a même une forme assez restreinte de la virtualité
pour les données :

struct A {} ;
struct B : A { int i ; } ;

int A::*pi = static_cast< int A::* >( &B::i ) ;

void
f( A* p )
{
std::cout << p->*pi ;
}

C'est légal, et ça marche, dans la mesure où l'objet désigné par
p a réelement un type dynamique B (ou quelque chose qui dérive
de B -- et je crois qu'il y a des restrictions dans le cas des
dérivations virtuelles).


As-tu déjà rencontré des cas concrets où ce genre de chose était utilisé ?
--
Loïc


Avatar
James Kanze
Loïc Joly wrote:

En passant, c'est intéressant à noter qu'avec des pointeurs au
membre, on a même une forme assez restreinte de la virtualité
pour les données :

struct A {} ;
struct B : A { int i ; } ;

int A::*pi = static_cast< int A::* >( &B::i ) ;

void
f( A* p )
{
std::cout << p->*pi ;
}

C'est légal, et ça marche, dans la mesure où l'objet désigné par
p a réelement un type dynamique B (ou quelque chose qui dérive
de B -- et je crois qu'il y a des restrictions dans le cas des
dérivations virtuelles).


As-tu déjà rencontré des cas concrets où ce genre de chose étai t utilisé ?


Oui:-(. En fait, si je le connais, c'est parce que j'ai
rencontré du code qui s'en servait. J'avais cru que c'était
illégal, j'ai cherché dans la norme pour trouver le passage
exact qui l'interdisait, et j'ai trouvé qu'au contraire, c'est
explicitement permis et supporté.

N'empèche que je ne m'en servirai pas.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34