libreria - list iterator c++
¿Cómo puedo exponer iteradores sin exponer el contenedor utilizado? (4)
He estado usando C # por un tiempo, y volver a C ++ es un dolor de cabeza. Estoy tratando de llevar algunas de mis prácticas de C # a C ++, pero estoy encontrando resistencia y me gustaría aceptar su ayuda.
Me gustaría exponer un iterador para una clase como esta:
template <class T>
class MyContainer
{
public:
// Here is the problem:
// typedef for MyIterator without exposing std::vector publicly?
MyIterator Begin() { return mHiddenContainerImpl.begin(); }
MyIterator End() { return mHiddenContainerImpl.end(); }
private:
std::vector<T> mHiddenContainerImpl;
};
¿Estoy intentando algo que no es un problema? ¿Debo simplemente escribir typedef std :: vector <T> :: iterator? Espero solo depender del iterador, no del contenedor de implementación ...
Esto debería hacer lo que quieras:
typedef typename std::vector<T>::iterator MyIterator;
Desde acelerado C ++ :
Siempre que tenga un tipo, como
vector<T>
, que depende de un parámetro de plantilla, y desea usar un miembro de ese tipo, comosize_type
, que es él mismo un tipo, debe anteponer el nombre completo portypename
a dejar que la implementación sepa tratar el nombre como un tipo.
He hecho lo siguiente antes, así que obtuve un iterador que era independiente del contenedor. Esto puede haber sido excesivo ya que también podría haber usado una API donde la persona que llama pasa en un vector<T*>&
debe llenarse con todos los elementos y luego la persona que llama puede simplemente iterar directamente desde el vector.
template <class T>
class IterImpl
{
public:
virtual T* next() = 0;
};
template <class T>
class Iter
{
public:
Iter( IterImpl<T>* pImpl ):mpImpl(pImpl) {};
Iter( Iter<T>& rIter ):mpImpl(pImpl)
{
rIter.mpImpl = 0; // take ownership
}
~Iter() {
delete mpImpl; // does nothing if it is 0
}
T* next() {
return mpImpl->next();
}
private:
IterImpl<T>* mpImpl;
};
template <class C, class T>
class IterImplStl : public IterImpl<T>
{
public:
IterImplStl( C& rC )
:mrC( rC ),
curr( rC.begin() )
{}
virtual T* next()
{
if ( curr == mrC.end() ) return 0;
typename T* pResult = &*curr;
++curr;
return pResult;
}
private:
C& mrC;
typename C::iterator curr;
};
class Widget;
// in the base clase we do not need to include widget
class TestBase
{
public:
virtual Iter<Widget> getIter() = 0;
};
#include <vector>
class Widget
{
public:
int px;
int py;
};
class Test : public TestBase
{
public:
typedef std::vector<Widget> WidgetVec;
virtual Iter<Widget> getIter() {
return Iter<Widget>( new IterImplStl<WidgetVec, Widget>( mVec ) );
}
void add( int px, int py )
{
mVec.push_back( Widget() );
mVec.back().px = px;
mVec.back().py = py;
}
private:
WidgetVec mVec;
};
void testFn()
{
Test t;
t.add( 3, 4 );
t.add( 2, 5 );
TestBase* tB = &t;
Iter<Widget> iter = tB->getIter();
Widget* pW;
while ( pW = iter.next() )
{
std::cout << "px: " << pW->px << " py: " << pW->py << std::endl;
}
}
No estoy seguro de lo que quiere decir con "no exponer std :: vector públicamente", pero de hecho, puede definir su typedef así:
typedef typename std::vector<T>::iterator iterator;
typedef typename std::vector<T>::const_iterator const_iterator; // To work with constant references
Podrás cambiar estos typedefs más tarde sin que el usuario note nada ...
Por cierto, se considera una buena práctica también exponer algunos otros tipos si desea que su clase se comporte como un contenedor:
typedef typename std::vector<T>::size_type size_type;
typedef typename std::vector<T>::difference_type difference_type;
typedef typename std::vector<T>::pointer pointer;
typedef typename std::vector<T>::reference reference;
Y si es necesario para tu clase:
typedef typename std::vector<T>::const_pointer const_pointer;
typedef typename std::vector<T>::const_reference const_reference;
Encontrarás el significado de todos estos typedef aquí: documentación de STL sobre vectores
Editar: se agregó el typename
como se sugiere en los comentarios
Puede encontrar el siguiente artículo interesante ya que aborda exactamente el problema que ha publicado: En la tensión entre programación orientada a objetos y genérica en C ++ y qué tipo de eliminación puede hacer al respecto