2010-10-12 11 views
5

Mam klasy, która ma std :: wektor wskaźnika kontroli podrzędnej. Z oczywistych powodów nie chcę, aby użytkownik klasy miał bezpośredni dostęp do std :: vector. Wszystko, czego bym chciał, to sposób na przekazanie rozmówcy wskazówek. Jaki byłby dobry sposób, aby to zrobić? (Ta funkcja zostanie wywołana często)Obiekt zorientowany na sposób iteracji poprzez std :: vector?

Dzięki

+6

Powinieneś zapomnieć o pomyśle, że "OO" to kolejne słowo oznaczające "dobre". Często tak nie jest. W szczególności, STL nie jest szczególnie zorientowany obiektowo, ale * jest * dobrze zaprojektowany. Problem, o który pytasz, ma niewiele wspólnego z OOP, a odpowiedź, którą otrzymujesz, nie dotyczy tak naprawdę "obiektowych" sposobów rozwiązania tego problemu. Iteratory to tylko * właściwy * sposób na zrobienie tego. Co jest o wiele ważniejsze niż to, czy są one "sposobem na wykonanie OOP. :) – jalf

+3

Cóż," dobry "ma w sobie" oo ":-) – Arun

+3

Tak samo jest" kupa ". –

Odpowiedz

14

Podaj funkcję, która zwraca const_iterator do wektora. Warto również dodać jeden, aby zwrócić iterator na koniec wektora.

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

więc po otrzymaniu tych użytkownik może zrobić dla (to; to!= itend; ++ it) {} – jmasterx

+0

@Milo: tak, to prawda. Ale uniemożliwiono by im zmianę wektora; mogli tylko to przeczytać. – JoshD

+0

Dzięki! to zadziała świetnie! – jmasterx

3

Iteratory to dobry, oczywisty sposób na zrobienie tego. Odwiedzający to kolejny sposób, aby dać kod klienta zdolność do działania na każdym elemencie w wektorze: w pewnym sensie jest nawet czystsze, odsłaniając mniej dla użytkownika i pozwala pojemnika większą kontrolę, np:

  • nie ma problemu z klientem, który ma iteratory, które mogą później zostać unieważnione:
  • w celu uzyskania blokady mutex, dopóki kod klienta nie przeczyta wszystkich pozycji, zanim inne wątki będą mogły działać na kontenerze
  • jeśli filtrujesz lub syntetyzujesz elementy, nie trzeba tworzyć skomplikowanych obiektów proxy pośredniczącego:

ALE

  • klient jest silniej zablokowana cokolwiek iteracja podać: na przykład zazwyczaj można przejść przez wiele niezależnych iteratorów za pomocą kontenera, ułatwiając operacje na wielu elementach, ale użytkownik zazwyczaj wykonuje jeden raz przed powrotem: każda dodatkowa funkcjonalność - zawieszenie/wznowienie iteracji, usunięcie elementu - musi być szczególnie obsługiwana przez kod wizyty kontenera (być może przez kod powrotu z funkcji odwiedzającego). (Nawet bez wyraźnego wsparcia zakończenie iteracji może zostać osiągnięte przez wyjątek). W przeciwieństwie do tego, w iteratorach można użyć pojedynczej funkcji kasowania na iteratorze, niezależnie od tego, czy od begin(), inkrementacji czy nie, a także innych operacji, takich jak find(): jest to czystszy faktoring funkcjonalności.

To będzie wyglądać następująco:

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

zwykle zrobić to coś podobnego do poniższego:

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

Następnie można po prostu zrobić coś takiego:

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

Nie potrzeba żadnych iteratorów do świata zewnętrznego. Jeśli kiedykolwiek musiałeś ujawnić coś takiego w standardowym API C, to bardzo łatwo jest to ujawnić.

+0

Zdecydowanie dobrze, aby ta odpowiedź była tutaj wymieniona, ale są też pewne problemy: interfejs wymagający dostępu losowego utrudnia zmienianie implementacji bez gwałtownego wpływu kodu klienta na klienta, dlatego nadal najlepiej jest udostępniać te funkcje jako a także iterator begin()/end() i/lub wsparcie dla odwiedzających. Bardziej zwykłe nazywać je size(), T & operator [] (size_t)/const T & operator [] (size_t) const. –

Powiązane problemy