web-dev-qa-db-fra.com

Comment la surcharge de l'opérateur arrow-> fonctionne en interne en c ++?

Je comprends la surcharge normale de l'opérateur. Le compilateur peut les traduire directement en appel de méthode. Je ne suis pas très clair sur l'opérateur ->. J'écrivais mon premier itérateur personnalisé et je ressentais le besoin de -> opérateur. J'ai jeté un œil au code source stl et j'ai implémenté le mien comme ça:

MyClass* MyClassIterator::operator->() const
{
    //m_iterator is a map<int, MyClass>::iterator in my code.
    return &(m_iterator->second);
}

Ensuite, je peux utiliser une instance de MyClassIterator comme:

myClassIterator->APublicMethodInMyClass().

On dirait que le compilateur fait deux étapes ici. 1. Appelez la méthode -> () pour obtenir une variable MyClass * temporaire. 2. Appelez APublicMethodInMyClass sur la variable temporaire en utilisant son opérateur ->.

Ma compréhension est-elle correcte?

39
Ryan
myClassIterator->APublicMethodInMyClass()

n'est rien d'autre que ce qui suit:

myClassIterator.operator->()->APublicMethodInMyClass()

Le premier appel à la surcharge operator-> Vous obtient un pointeur d'un certain type qui a une fonction membre accessible (depuis votre site d'appel) appelée APublicMethodInMyClass(). Les règles habituelles de recherche de fonction sont suivies pour résoudre APublicMethodInMyClass(), bien sûr, selon qu'il s'agit d'un virtuel ou non.

Il n'y a pas nécessairement une variable temporaire; le compilateur peut copier ou non le pointeur retourné par &(m_iterator->second). Selon toute probabilité, cela sera optimisé. Cependant, aucun objet temporaire de type MyClass ne sera créé.

Les mises en garde habituelles s'appliquent également à m_iterator - assurez-vous que vos appels n'accèdent pas à un itérateur invalidé (c'est-à-dire si vous utilisez vector par exemple).

30
dirkgently

Le operator-> A une sémantique spéciale dans le langage en ce que, lorsqu'il est surchargé, il se réapplique au résultat. Alors que les autres opérateurs ne sont appliqués qu'une seule fois, operator-> Sera appliqué par le compilateur autant de fois que nécessaire pour accéder à un pointeur brut et une fois de plus pour accéder à la mémoire référencée par ce pointeur.

struct A { void foo(); };
struct B { A* operator->(); };
struct C { B operator->(); };
struct D { C operator->(); };
int main() {
   D d;
   d->foo();
}

Dans l'exemple précédent, dans l'expression d->foo() le compilateur prendra l'objet d et lui appliquera operator->, Ce qui donnera un objet de type C , il réappliquera ensuite l'opérateur pour obtenir une instance de B, réappliquera et obtiendra A*, après quoi il déréférencera l'objet et obtiendra les données pointées.

d->foo();
// expands to:
// (*d.operator->().operator->().operator->()).foo();
//   D            C            B           A*