2010-10-12 16 views
5

Tengo una clase que tiene un std :: vector de puntero de control infantil. Por razones obvias, no quiero que el usuario de la clase tenga acceso directo al std :: vector. Todo lo que quisiera es una forma de darle al que llama los consejos. ¿Cuál sería una buena forma de OO para hacer esto? (Esta función se llama a menudo)forma orientada a objetos para iterar a través de un estándar :: vector?

Gracias

+6

Debe olvidar la idea de que "OO" es otra palabra para "bien". A menudo no lo es. Específicamente, el STL no está especialmente orientado a objetos, pero * está * bien diseñado. El problema por el que estás preguntando tiene poco que ver con OOP, y la respuesta que estás obteniendo tampoco se trata de formas "orientadas a objetos" para resolverlo. Los iteradores son la forma * correcta * de hacerlo. Lo cual es mucho más importante que si son o no una "forma de OOP de hacerlo. :) – jalf

+3

Bueno," bueno "tiene" oo "incrustado en él :-) – Arun

+3

Lo mismo ocurre con" caca " –

Respuesta

14

proporcionar una función que devuelve un const_iterator al vector. También es útil agregar uno para devolver el iterador al final del vector.

class MyClass { 
public: 
    typedef vector<T>::const_iterator c_iter; 

    c_iter getBegin() const {return v.begin();} 
    c_iter getEnd() const {return v.end();} 

    // and perhaps if it's useful and not too invasive. 
    const T& getAt(int i) const {return v.at(i);} 

    //stuff 
    vector<T> v; 
}; 
+0

luego de obtener estos el usuario podría hacer por (it; it!= itend; ++ it) {} – jmasterx

+0

@Milo: sí, eso es correcto. Pero se les impediría alterar el vector; solo podían leerlo. – JoshD

+0

¡Gracias! esto funcionará genial! – jmasterx

3

Los iteradores son una forma buena y obvia de hacerlo. Un patrón de visitante es otra forma para dar el código de cliente la capacidad de operar en cada elemento en el vector: en algunos aspectos es aún más limpio, exponiendo menos para el usuario, y permitiendo que el contenedor más control, por ejemplo:

  • no hay problema con el cliente que tenga iteradores que podrían ser invalidados
  • para obtener un bloqueo mutex hasta que el código del cliente haya leído todas las entradas antes de que otros subprocesos puedan operar en el contenedor
  • si filtra o sintetiza los elementos, no necesita crear objetos proxy de iterador complicados

PERO

  • el cliente está más fuertemente bloqueado en cualquier iteración usted proporciona: por ejemplo, Generalmente, puede pasar múltiples iteradores independientes a través de un contenedor, facilitando las operaciones en múltiples elementos, pero el visitante normalmente ejecuta una vez antes de regresar: cualquier funcionalidad adicional (suspendiendo/reanudando iteración, eliminando un elemento) necesita ser respaldada específicamente por el código de visita del contenedor (quizás por un código de retorno de la función de visitante). (Incluso sin un soporte explícito, la iteración final puede lograrse mediante una excepción). Por el contrario, con los iteradores se puede utilizar una sola función de borrado en un iterador, ya sea desde begin(), incrementado o no, así como en otras operaciones como find(): se trata de una factorización más limpia de la funcionalidad.

Eso sería algo como:

class Container 
{ 
    public: 
    template <typename Visitor> 
    void visit(Visitor& visitor) 
    { 
     for (Vector::const_iterator i = v_.begin(); i != v_.end(); ++i) 
      visitor(*i); 
    } 

    private: 
    typedef std::vector<X> Vector; 
    Vector v_; 
}; 

// client code... 

struct Visitor 
{ 
    void operator()(const X&) { ... } 
    // any data you want to update as you iterate... 
}; 

Visitor v(...any construction arguments...); 
container.visit(v); 
1

por lo general lo hago algo como lo siguiente:

class MyClass { 
public: 
    const unsigned int GetNumberOfItems() { return v.size(); } 

    T* GetItemNumber(const unsigned int n) 
    { 
    // 3 options here, thrown your own exception type, or use the std one, or 
    // or just return NULL meaning nothing there or out of range. 
    try{ 
     return v.at(n); 
    } catch (std::out_of_range &e){ 
    } 

    return NULL;  
    } 

    vector<T> v; 
}; 

continuación, sólo puede hacer algo como:

MyClass cl; 
int count = cl.GetNumberOfItems(); 
for (int i = 0; i < cl.GetNumberOfItems(); i++){ 
    T* item = cl.GetItemNumber(i); 
} 

No se requieren iteradores para el mundo exterior. Si alguna vez tiene que exponer algo como esto a una API C estándar, entonces es muy fácil exponer.

+0

Definitivamente es bueno tener esta respuesta listada aquí, pero también hay algunos problemas: una interfaz que requiere acceso aleatorio hace que sea más difícil variar la implementación sin que el código del cliente tenga repentinamente un impacto de rendimiento, por lo que es mejor proporcionar estas funciones como así como un iterador de inicio()/final() y/o soporte de visitante. Más habitual es llamarlos tamaño(), T y operador [] (tamaño_t)/const T & operador [] (tamaño_t) const. –

Cuestiones relacionadas