Объектно-ориентированный способ перебора std :: vector? - PullRequest
5 голосов
/ 12 октября 2010

У меня есть класс, у которого есть std :: vector указателя дочернего элемента управления.По понятным причинам я не хочу, чтобы пользователь класса имел прямой доступ к std :: vector.Все, что я хотел бы, это способ указать вызывающему абоненту указатели.Какой хороший ОО способ сделать это?(эта функция будет часто вызываться)

Спасибо

Ответы [ 3 ]

14 голосов
/ 12 октября 2010

Предоставляет функцию, которая возвращает const_iterator вектору.Также полезно добавить единицу, чтобы вернуть итератор в конец вектора.

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;
};
3 голосов
/ 12 октября 2010

Итераторы - это хороший, очевидный способ сделать это.Шаблон посетителя - это еще один способ дать клиентскому коду возможность работать с каждым элементом в векторе: в некоторых отношениях он даже чище, меньше подвергается воздействию пользователя и позволяет контейнеру больше контроля, например:

  • нет проблем с клиентом, имеющим итераторы, которые могут впоследствии быть признаны недействительными
  • для получения блокировки мьютекса, пока код клиента не прочитает все записи, прежде чем другим потокам будет разрешено работать с контейнером
  • если вы фильтруете или синтезируете элементы, вам не нужно создавать сложные прокси-объекты итератора

НО

  • клиент более сильно привязан к любой итерации, которую вы предоставляетеНапример, вы можете пропустить несколько независимых итераторов через контейнер, упрощая операции с несколькими элементами, но посетитель обычно проходит один раз перед возвратом: любая дополнительная функциональность - приостановка / возобновление итерации, удаление элемента - должна специально поддерживаться контейнером viсидячий код (возможно, по коду возврата из функции посетителя).(Даже без явной поддержки завершение итерации может быть достигнуто исключением).В отличие от этого, с итераторами для итератора может использоваться одна функция стирания, начиная с begin(), с приращением или без, а также с другими операциями, такими как find(): это более чистый факторинг функциональности.

Это будет выглядеть примерно так:

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 голос
/ 13 октября 2010

Я обычно делаю что-то вроде следующего:

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

Тогда вы можете просто сделать что-то вроде:

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

Никаких итераторов для внешнего мира не требуется. Если вам когда-либо приходилось выставлять что-то подобное стандартному C API, тогда это очень легко разоблачить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...