2010-10-12 13 views
5

Ho una classe che ha un puntatore std :: vector di controllo figlio. Per ovvi motivi, non voglio che l'utente della classe abbia accesso diretto a std :: vector. Tutto ciò che vorrei è un modo per dare al chiamante le indicazioni. Quale sarebbe un buon modo OO per fare questo? (Questa funzione sarà chiamata spesso)Modo orientato agli oggetti per iterare attraverso un vettore std ::?

Grazie

+6

Si dovrebbe dimenticare l'idea che "OO" è un'altra parola per "buono". Spesso non lo è. Nello specifico, lo STL non è particolarmente orientato agli oggetti, ma * è * ben progettato. Il problema che stai chiedendo ha poco a che fare con OOP, e la risposta che stai ricevendo non riguarda in realtà i modi "orientati agli oggetti" per risolverlo. Gli iteratori sono solo il modo * giusto * per farlo. Che è molto più importante del fatto che siano o meno un modo "OOP di farlo. :) – jalf

+3

Bene," buono "ha" oo "incorporato in esso :-) – Arun

+3

Così fa" cacca ". –

risposta

14

fornire una funzione che restituisce un const_iterator al vettore. È anche utile aggiungerne uno per restituire l'iteratore alla fine del vettore.

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

così dopo aver ottenuto questi l'utente potrebbe fare per (esso; esso!= itend; ++ it) {} – jmasterx

+0

@Milo: sì, è vero. Ma a loro sarebbe impedito di alterare il vettore; potevano solo leggerlo – JoshD

+0

Grazie! questo funzionerà alla grande! – jmasterx

3

Gli iteratori sono un modo valido e ovvio per farlo. Un modello visitatore è un altro modo per fornire il codice cliente la possibilità di operare su ciascun elemento del vettore: per certi versi è ancora più pulito, esponendo meno per l'utente, e permettendo il contenitore più controllo, per esempio:

  • non vi è alcun problema con il client che abbia effetti iteratori successivamente invalidato
  • per ottenere un blocco mutex finché il codice client non ha letto tutte le voci prima che altri thread possano operare sul contenitore
  • se si filtrano o sintetizzano gli elementi, non è necessario creare complicati oggetti proxy iteratori

MA

  • il cliente è più fortemente bloccato in qualunque iterazione che fornisci: per esempio in genere è possibile eseguire più iteratori indipendenti tramite un contenitore, facilitando le operazioni su più elementi, ma il visitatore di solito esegue una volta prima di tornare: qualsiasi funzionalità aggiuntiva - sospendere/riprendere l'iterazione, eliminare un elemento - deve essere specificamente supportata dal codice di visita del contenitore (forse da un codice di ritorno dalla funzione visitatore). (Anche senza supporto esplicito, l'interruzione dell'iterazione potrebbe essere ottenuta con un'eccezione). Per contrasto, con gli iteratori è possibile utilizzare una singola funzione di cancellazione su un iteratore sia da begin(), incrementato o meno, sia con altre operazioni come find(): si tratta di una più chiara funzionalità di factoring.

Che sarebbe simile:

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

Io di solito lo fanno qualcosa di simile a quanto segue:

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; 
}; 

Poi si può solo fare qualcosa di simile:

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

Non sono richiesti iteratori per il mondo esterno. Se hai mai dovuto esporre qualcosa di simile a una API C standard, è molto facile esporlo.

+0

Decisamente buono per avere questa risposta elencata qui, ma ci sono anche alcuni problemi: un'interfaccia che richiede l'accesso casuale rende più difficile variare l'implementazione senza che il codice client subisca improvvisamente un problema di prestazioni, quindi è sempre meglio fornire queste funzioni come beh come un iteratore di inizio()/fine() e/o supporto per i visitatori. Più normale chiamarli size(), T & operator [] (size_t)/const T & operator [] (size_t) const. –

Problemi correlati