2008-10-01 16 views
22

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.¿Cómo puedo exponer iteradores sin exponer el contenedor utilizado?

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 tratando en algo que no es un problema? ¿Debo simplemente escribir typedef std :: vector < T> :: iterator? Estoy esperando en apenas dependiendo del iterador, no el contenedor implementar ...

+0

Véase también esta [pregunta] (http://stackoverflow.com/questions/127009/returning-an-any-kind-of-input-iterator-instead-of-a-vectoriterator-or-a -listit) –

Respuesta

2

Esto debería hacer lo que quiera:

typedef typename std::vector<T>::iterator MyIterator; 

De Accelerated C++:

Siempre que tenga un tipo, como vector<T>, que depende de un parámetro de plantilla, y desea utilizar un miembro de ese tipo, como size_type, que es en sí mismo un tipo, debe preceder al nombre completo por typename para que la implementación sepa que trata el nombre como un tipo.

1

estoy seguro de lo que quiere decir con "no exponer std :: vector públicamente", pero de hecho, sólo 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 

usted será capaz de cambiar estos typedefs más tarde sin que el usuario darse cuenta de nada ...

Por cierto, se considera una buena práctica para exponer también 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 por su clase:

Encontrará el significado de todo esto typedef está aquí: STL documentation on vectors

Editar: Se ha añadido la typename como se sugiere en los comentarios

+0

Tal vez estoy exagerando un poco esto, pero me gustaría tener el typedef para simplemente exponer que estoy usando stl iterators, no el contenedor real. Si hago typedef std :: vector :: iterator iterator, entonces la gente puede hacer std :: vector <ínt> :: iterator iter = example.Begin() ;. (continuación) – Statement

+0

Si bien esto no parece ser un problema al principio, imagine si cambio la implementación interna de mi clase para usar una lista.El código del cliente se romperá. Usar un iterador común que funcione con muchos contenedores diferentes resolvería este problema. El problema es que no he encontrado la forma de hacerlo. – Statement

+0

Poca corrección en su publicación: como T es un parámetro de plantilla, debe usar la palabra clave typename en su typedefs, es decir, typedef typename std :: vector :: iterator iterator; –

2

He hecho lo siguiente antes, así que t 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*>& que debe estar lleno 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; 
    } 
} 
+0

Bien, pero no es un iterador estándar. Prefiero no reinventar la rueda. Otros ya se espera que sean utilizados para los iteradores estándar, sin curva de aprendizaje allí. –

Cuestiones relacionadas