Escribir iteradores usted mismo casi nunca es bonito. La forma más pretendida de agregar un iterador a sus clases es reutilizar una existente. Debe tener en cuenta que los punteros, por ejemplo, son tan buenos como los iteradores en C++, por lo que hay muchas formas de proporcionar un iterador sin tener que escribir el suyo.
Esto es básicamente cómo funciona C++ de muchas maneras. Trata de hacer que el lenguaje sea prescindible y simple para los usuarios finales al poner una gran carga en los escritores de la biblioteca. Es decir. los escritores de bibliotecas pueden escribir todo lo poco atractivo, por lo que el usuario final no tiene que hacerlo. Los iteradores son usualmente parte de una biblioteca.
Dicho esto, aquí viene la parte fea real:
Para ser capaz de escribir sus propios iteradores, aquí hay algunas cosas que hay que tener en cuenta.
rasgos
Tipo:
rasgos de tipo son un mecanismo simple para añadir información adicional a los tipos en C++ que funciona incluso con tipos que no pueden cambiarse a sí mismos. Por ejemplo, para un iterador es importante saber qué itera (es decir, el tipo contenido). La forma de obtener esta información para un iterador dado depende mucho del iterador. Para los iteradores que en realidad son objetos, puede agregar typedefs en la clase y usarlos, pero para los iteradores que son punteros, debe deducirlo del tipo de puntero. Para que esto sea posible, la información se almacena en un rasgo de tipo, de modo que hay un solo lugar donde un código puede ver esta información. Este es el rasgo de tipo std::iterator_traits
.
std::iterator_traits
funcionan en cualquier cosa, que se deriva de la plantilla std::iterator
, así como en cualquier tipo de puntero, sin ningún ajuste. Muy a menudo lo mejor es usar std::iterator
como base para evitar tener que escribir su propia especialización de rasgos. En caso de que no pueda hacer esto, aún es posible proporcionar los rasgos necesarios, pero será más difícil.
clases y tipos de etiquetas de iterador:
Hay varios tipos diferentes de iteradores disponibles en C++ que tienen un comportamiento diferente y puede/no se puede hacer un montón de cosas diferentes. Eche un vistazo al http://cplusplus.com/reference/std/iterator/ para ver qué tipo de iteradores están disponibles y qué pueden hacer. Los diagramas no están destinados a ser orientados a objetos (es decir, un input_iterator
no es una subclase ni una clase base de forward_iterator
), sino más bien como un tipo de derivación de API. Es decir. puede usar todos los algoritmos que se escribieron para un iterador de entrada también con un iterador directo. La tabla en la página le dirá qué métodos debe proporcionar para cada categoría.
Dado que estas categorías no son realmente subcategorías entre sí (no deberían serlo, especialmente cuando proceden de diferentes tipos de colecciones), se utiliza otro mecanismo para identificar las capacidades de cada iterador. También se incluye una clase de etiqueta vacía en el std::iterator_traits
que describe cada iterador, que indica qué puede hacer este iterador y qué no puede hacer. Si no escribe sus propios rasgos, debe proporcionar esta clase de etiqueta a la plantilla std::iterator
al crear instancias.
Ejemplo:
Este ejemplo se toma de la cplusplus.com en la sección iteradores:
class myiterator : public iterator<input_iterator_tag, int>
{
int* p;
public:
myiterator(int* x) :p(x) {}
myiterator(const myiterator& mit) : p(mit.p) {}
myiterator& operator++() {++p;return *this;}
myiterator operator++(int) {myiterator tmp(*this); operator++(); return tmp;}
bool operator==(const myiterator& rhs) {return p==rhs.p;}
bool operator!=(const myiterator& rhs) {return p!=rhs.p;}
int& operator*() {return *p;}
};
Este iterador no tiene mucho sentido, ya que sólo se ajusta un puntero, que también podría haber sido utilizado directamente. Sin embargo, puede servir como una explicación. El iterador se deriva de std::iterator
como input_iterator
proporcionando la etiqueta adecuada. También se le dice a la plantilla que este iterador está iterando sobre int
s. Todos los demás tipos, que son necesarios difference_type
, reference
, poiner
etc. son automáticamente introducidos por la plantilla. En algunos casos, puede tener sentido cambiar algunos de estos tipos manualmente (por ejemplo, un std::shared_ptr
se debe usar a veces como pointer
). Además, los rasgos necesarios para este iterador existirán automáticamente, dado que ya se derivan de std::iterator
y std::iterator_traits
saben dónde encontrar toda la información necesaria.
La forma más sencilla es probablemente usar 'iterator_facade' de [Boost.Iterator] (http://www.boost.org/doc/libs/release/libs/iterator/doc/iterator_facade.html). –
Incluso más simple sería usar boost :: filesystem y evitar tener que escribir cualquier código ... –
bien, debería haber sabido que debería haber agregado la información, que no quiero usar boost;) Mayormente, porque Quiero entender cómo funciona un iterador. – Ben