2010-12-19 10 views
5

Mi tarea actual es escribir una lista con iteradores. La lista no está siendo un problema sino la creación de la clase de iterador.Semántica de -> operador en listas (y en general C++)

De un par de fuentes he visto que tengo dos operadores para definir en mi clase de iterador: operator* y operator->.

Genial hasta el momento! Suponiendo que la estructura de mi iterador es tan

// Nested class of List 
class _Iter 
{ 
private: 
    ListElem *pCurr; 
    const List *pList; 

public: 
    _Iter(ListElem *pCurr, const List *list) 
     : pCurr_(pCurr), pList(list) 
    {} 

    T& operator*() { return pCurr_->data; } 
    T* operator->() { return &**this; } 
}; 

con ListElem siendo

// Nested struct of List 
struct ListElem 
{ 
    T data; 

    ListElem *next; 
    ListElem *prev; 
}; 

puedo ver que estoy haciendo algo mal masivamente (como el doble desreferencia de que esto llevaría a un (* pCurr _-> Datos &), que no es desreferenciable.

Mi problema principal es no comprender qué -> se supone que debe hacer en este caso. ¿Debería otorgarle acceso al usuario a la clase ListElem? Si ese es el caso, ¿por qué no puedo? solo escriba

ListElem *operator->() { return pCurr_; } 

¿En lugar de devolver un puntero? Mi comprensión de estos dos operadores, como se usa en mi lista (y esperemos que las listas STL) es que:

operator*() // Return data pointed to by iterator; pCurr_->data; 
operator->() // Grant access to data-holding structure; pCurr; 

¿Es esto correcto, o lo que no recibo? (¿Y -> tiene un nombre propio?)

Respuesta

5

Hagas lo que hagas, (*something).somethingElse debería ser equivalente a something->somethingElse. El último es solo una sintaxis breve para el primero. Por lo tanto,

T& operator*() { return pCurr_->data; } 
T* operator->() { return &**this; } 

está bien porque simplemente elimina referencias *thisthis que tiene el tipo _Iter*, no _Iter, por lo que se hace sin operator*() llamada.Luego desreferencia *this, por lo que obtiene pCurr->data, luego toma su dirección, por lo que obtiene & pCurr-> data. Pero sería mucho más clara que acaba de escribir:

T& operator*() { return pCurr_->data; } 
T* operator->() { return &pCurr->data; } 

Ahora bien, esta

ListElem *operator->() { return pCurr_; } 

es incorrecto porque si operator*() vuelve T&, operator->() deben volver T*, eso es lo que fue diseñado. Si realmente desea otorgar acceso a ListItem en lugar de sus datos (que pueden o no tener sentido según el diseño, pero en su caso parece que no), entonces también debe redefinir operator*() para obtener esto:

ListElem& operator*() { return *pCurr_; } 
ListElem *operator->() { return pCurr_; } 

Tenga en cuenta que no es un requisito de idioma, es solo la forma en que diseña su clase para evitar una interfaz confusa.

+0

De hecho, a menudo olvido que 'this' es en realidad un puntero. Muy molesto: D – IAE

1

operator-> da un puntero al objeto apuntado por el iterador, en este caso (aparentemente) pCurr_->data.

T *operator->() { return &(pCurr_->data); } 

debe devolver la dirección del objeto devuelto por operator*() como un valor o referencia.

T &operator*() { return pCurr_->data; } 
// or 
T &operator*() { return *operator->(); } 

operator->() existe para implementar -> con iteradores (con el comportamiento que tiene para los punteros) y es necesario porque operator* puede devolver un objeto por valor en lugar de por referencia.

Tenga en cuenta que no necesita almacenar un puntero al List en el iterador para obtener la funcionalidad requerida.

+1

He actualizado mi pregunta con struct ListElem. Y aaaah, al proporcionar un puntero a Foo ahora podemos usar todos los métodos de Foos. ¡Así que no estoy devolviendo el ListElem en sí mismo, sino pasando la funcionalidad del miembro de datos Foo al usuario! Eso tiene sentido. – IAE

+0

@SoulBeaver: actualicé mi respuesta. –

+0

Sí, está devolviendo el elemento de datos subyacente para que lo use el cliente. Recuerde que los iteradores son una generalización de punteros y apuntan de manera similar a un elemento de datos (a veces de forma muy indirecta; yo diseñé un iterador que contiene punteros a objetos polimórficos que a su vez contienen varios tipos de iteradores). –

1

su directriz principal debe ser que

(*iter).hello(); 
iter->hello(); 

debe tanto hacer lo mismo. Eso es lo que el usuario espera. La devolución de ListElem no le da nada al usuario. El usuario ni siquiera debería conocer los detalles de la implementación de ListElem.