Question

Je suis habitué à penser que les fonctions membres ne sont qu'un cas particulier des fonctions normales, où les fonctions membres ont un paramètre supplémentaire au début de leur liste de paramètres pour le pointeur 'this', c'est-à-dire l'objet sur lequel la fonction membre est censée agir. J'ai déjà utilisé boost :: function de cette façon et je n'ai jamais rencontré de problèmes:

boost::function f<(void)(MyObject*, int, int)> = &MyObject::method_that_takes_two_ints;

Mais j'ai vu cette syntaxe pour les pointeurs de fonction de membre:

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

Dans cette syntaxe, le paramètre 'this' n'est pas visible. Ce qui m'a amené à me demander si, sous le capot, les fonctions pointeur vers membre sont vraiment une bête à part, et que cette amélioration prenait soin des détails pour moi.

Qu'est-ce que la norme dicte quant à l'emplacement du paramètre "this"? Peut-être que juste sur mon compilateur, l'argument supplémentaire 'this' vient en premier, et peut-être sur d'autres compilateurs, il pourrait être à la fin? Suis-je simplement chanceux que ma façon de penser soit conforme à la façon dont mes compilateurs (GCC4, VS2005) le traitent? Les fonctions pointeur-à-membre sont-elles toujours simplement un cas particulier de pointeur-fonctions-fonctions avec un paramètre supplémentaire ou le compilateur peut-il les implémenter différemment?

Était-ce utile?

La solution

La norme ne dit pratiquement rien sur l'endroit où le pointeur ce doit être placé. En fait, il est assez courant d'utiliser une convention d'appel différente pour les fonctions membres. (Le pointeur 'this' n'est donc pas simplement un premier argument supplémentaire, il est en fait stocké dans un emplacement différent de celui du premier argument.)

MSVC utilise notamment la convention d'appel thiscall pour les fonctions membres et stdcall ailleurs. http://www.hackcraft.net/cpp/MSCallingConventions/#thiscall décrit les différences entre eux, mais notez que thiscall stocke le ce pointeur dans le registre ECX , tandis que stdcall stocke < em> tous les paramètres de la pile.

Vous feriez mieux de les traiter comme des types complètement distincts. Un pointeur sur une fonction membre est pas simplement un pointeur sur une fonction avec un paramètre supplémentaire.

Autres conseils

Le pointeur this n'est pas stocké avec un pointeur sur membre (les pointeurs de fonctions membres en sont un cas particulier) Si vous venez de faire

void (MyObject::*f)( int, int ) = &MyObject::method_that_takes_two_ints;

alors ce qui est stocké est juste l'information que la fonction membre devrait être appelée sur un objet que vous devrez fournir plus tard. Si vous voulez l'appeler, vous devez passer un objet où le compilateur obtiendra le pointeur this .

MyObject o; (o.*f)(1, 2);

Un pointeur de fonction membre est juste un pointeur de membre dont le type (qui est pointé) est un type de fonction. La norme indique que les pointeurs de fonction membre ne possèdent pas leur propre "type de fonction membre". qu'ils désignent et qui incluraient en quelque sorte le type de pointeur.

int main() {
    typedef void fun() const;
    fun MyObject::*mem_function_ptr = 
        &MyObject::const_method_that_takes_two_ints;
}

fun dans ce code correspond au type de fonction. Le type qui est "normal" la fonction a. Un pointeur sur une fonction, par opposition à un pointeur membre-fonction, est simplement un pointeur sur une fonction ayant ce type:

void foo() { cout << "hello"; }
int main() {
    typedef void fun();
    fun * f = &foo;
}

Alors qu’un pointeur sur une fonction membre a le niveau supplémentaire de pointeur membre sur ce type de fonction.

Quelque chose à propos du pointeur ce et à sa relation avec l'objet sur lequel il pointe (pas de technique, juste de la théorie):

Chaque fonction membre possède un paramètre caché appelé paramètre d'objet implicite , de type MyObject & amp; ou MyObject const & amp; , selon que vous l'ayez ou non. une fonction membre const ou nonconst. L'objet sur lequel vous appelez la fonction membre, o , est l'argument de l'objet implicite , qui est transmis au paramètre. Dans la théorie du standard qui compose les règles décrivant la manière dont les fonctions membres sont appelées, le paramètre d'objet implicite est un premier paramètre caché. C'est conceptuel, et ne signifie pas que c'est le cas réel dans les implémentations. L'argument d'objet implicite est ensuite lié à ce paramètre d'objet implicite, ce qui peut éventuellement entraîner des conversions implicites (ainsi, si vous appelez une fonction membre const sur un objet non-const, une conversion de qualification est convertie de MyObject en . MyObject const & amp; . C’est ce qui fait des fonctions non const un meilleur choix que les fonctions const à appeler, pour un objet non-const). Par exemple, on peut dire dans ce code:

struct A {
    operator int() const { return 0; }
};

int main() { 
    A a;
    int i = a; // implicit conversion using the conversion function
}

Que l'argument d'objet implicite a de type A est lié au paramètre d'objet implicite de type A const & amp / , dont l'objet est alors pointé par le ce pointeur ayant le type A const * ici. Il est important de noter que le paramètre d'objet implicite n'est qu'une construction théorique, afin de formaliser la manière dont les règles pour appeler une fonction membre sont constituées (et que les constructeurs ne les incluent pas), alors que le pointeur this existe réellement. this est un pointeur, car lorsque this a été introduit, C ++ n'avait pas encore de références.

J'espère que cela vous aidera à comprendre le problème.

Un excellent article sur les pointeurs de fonction de membre est Pointeurs de fonction de membre et le plus rapide possible C ++ Délégués . Cet article décrit les pointeurs de fonction de membre à partir des cas simples jusqu'aux pointeurs de fonction de membre virtuel à héritage multiple. En prime, il fournit une implémentation de délégués qui peut être vraiment utile.

Oui, les pointeurs vers les fonctions et les pointeurs vers les membres sont des bêtes complètement différentes. Les pointeurs vers les membres doivent disposer d'une instance d'objet à déréférencer à l'aide des opérateurs - > * ou . * . Aucun paramètre this n'est utilisé lors de la création d'un pointeur sur un membre, car this est déterminé lorsque le pointeur sur membre est utilisé (l'objet situé à gauche de - > * ou . * ).

Notez qu'il existe probablement moins de différence entre une fonction pointeur vers membre et une variable pointeur vers membre qu'entre une fonction pointeur vers membre et un pointeur de fonction normale.

Typiquement, les fonctions membres et les fonctions standard peuvent avoir des conventions d'appel complètement différentes afin que vous ne puissiez pas les répartir entre elles.

Notez que la taille d'un pointeur sur une fonction membre peut être différente en utilisant des compilateurs différents.

Une autre chose à noter, telle que décrite dans The Old New Blog de choses :

  

La taille d'un   le pointeur vers la fonction membre peut changer   en fonction de la classe.

Ce sont définitivement des types distincts et toutes les hypothèses que vous ferez seront spécifiques à la plate-forme / au compilateur.

Cette page contient davantage d'informations sur la mise en œuvre des points de fonction des membres que J'ai toujours voulu savoir, y compris les détails de mise en œuvre pour de nombreux compilateurs populaires.

Pour répondre à toutes les questions: Oui, ce sont des pointeurs spéciaux, différents des pointeurs ordinaires. Oui, boost :: function les reconnaît.

La norme ne dit rien sur les détails internes des piles d'appels. En fait, de nombreux compilateurs peuvent utiliser des registres d’entier, des registres à virgule flottante et / ou la pile en fonction de la liste des arguments. Un pointeur "ceci" n'est qu'un autre cas particulier.

Boost :: function résout ce problème en utilisant deux chemins de code en interne. Vous pouvez le voir en inspectant la pile d'appels pour les deux cas. Si votre boost :: function stocke un pointeur sur une fonction membre, l'opérateur () divisera la liste des arguments. Le premier argument est utilisé comme objet sur lequel la fonction membre est appelée avec les arguments restants.

Pour compléter la réponse de chacun, Boost.Function fonctionne en spécialisant l’opérateur d’affectation sur les pointeurs de fonction membre, afin de lui permettre de détecter le moment où vous en avez passé un. Lorsque vous appelez cette fonction, elle est réinterprétée en interne selon la méthode appropriée pour appeler le pointeur de la fonction membre ( (obj- > * fun) (args) ).

Je pense que ce lien pourrait vous intéresser:

http://www.parashift.com/c++ -faq-lite / pointeurs-aux-membres.html

C'est une très bonne description de tout ce que vous voulez savoir sur le pointeur sur les membres.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top