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?
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).
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*