Быстрый и гибкий итератор для абстрактного класса - PullRequest
2 голосов
/ 18 октября 2010

Для быстрого и гибкого обхода сеток с данными я создал абстрактный шаблонный класс GridDataStructure. Данные должны быть доступны для итераторов STL. Когда кто-то использует класс, он не должен беспокоиться о том, какой тип STL-итератора подходит для определенного подкласса.

Решением этой проблемы является Использование итераторов для скрытия внутреннего контейнера и выполнения общей операции над базовым контейнером . Однако я не понимаю, почему члены begin () и end () больше не являются виртуальными. Кроме того, я не мог понять, где именно должны быть реализованы необходимые методы для класса итераторов STL (например, оператор ++, оператор * и т. Д.).

Не могли бы вы посмотреть, сделал ли я ошибку в дизайне или нет? Для меня важен гибкий дизайн, но не за счет производительности.

Мой класс дизайна:

template<class T>
class GridDataStructure
{
public:
    virtual iterator begin() = 0;
    virtual iterator end() = 0;
};

template<class T>
class GridDataUniform : GridDataStructure
{
public:
    GridDataUniform(int size);        

    iterator begin();
    iterator end();

    class iterator : public std::iterator<std::forward_iterator_tag, T> {
    public:
      iterator(Node* p) : node_(p) {}
      ~iterator() {}

      iterator& operator=(const iterator& other);
      bool operator==(const iterator& other);
      bool operator!=(const iterator& other);
      iterator& operator++();
      iterator& operator++(int);
      T& operator*();
      T* operator->();

    private:
      Node* node_;
    };

    private:
        T* griddata;
};

Я хотел бы получить доступ к своему сеточному контейнеру в стиле STL, например:

GridDataStructure<int>::iterator = someGrid->begin(); // where someGrid is an instance of GridDataUniform
std::cout << *(iter) << std::endl;

Любая помощь высоко ценится.

Редактировать (19.10.10): добавлен вложенный класс итераторов

Редактировать (20.10.10): Добавлен код:

template<class T>
class GridDataStructureBase
{
protected:
class BaseIteratorImpl
{
    virtual iterator begin() = 0;
    virtual iterator end() = 0;
    virtual iterator& operator++() = 0;
}

public:
class iterator : std::iterator<std::forward_iterator_tag, T>
{
public:
    iterator(const BaseIteratorImpl& itImpl) {}
    iterator begin() { return itImpl->begin(); }
    iterator end() { return itImpl->end(); }
    iterator& operator++() { return itImpl->operator++() }

private:
    BaseIteratorImpl* itImpl;

};

iterator begin()
{
    iterator* i = new iterator(??);
    return i->begin();
}

iterator end()
{
    return iterator(NULL);
}

};

1 Ответ

4 голосов
/ 19 октября 2010

В решении начало и конец не должны быть виртуальными, потому что они просто вызывают виртуальные BaseIteratorImpl::begin и BaseIteratorImpl::end.

В вашем конкретном случае вы можете просто сделать begin и end виртуальными и не выполнять переадресацию, и он сможет делать то, что вы хотите. Решение, на которое вы указали, заключается в том, что вы хотите использовать разные итераторы стиля в одной и той же структуре, а не просто пары структура-итератор, которые, как вам кажется, нужны.

РЕДАКТИРОВАТЬ: Вот кое-что для начала (не проверено или даже скомпилировано) - может не скомпилироваться и возникнет утечка (пишите деструкторы, копируйте ctors, op =, где вам нужно) - просто чтобы вы начали работать над идеей .

template <class T>
class GridIteratorImplBase {
   public:
      virtual GridIteratorImplBase<T>& operator++() = 0;
      virtual T& operator*() = 0;
};

template <class T>
class GridIterator {
   private:
      GridIteratorImplBase<T> *baseImpl;
   public:
      GridIterator(GridIteratorImplBase<T> *b) :baseImpl(b) {}
      GridIterator& operator++() { baseImpl->operator++(); return *this;}
      T& operator*() { return baseImpl->operator*(); }


  // you need to write a dtor, copy ctor and op= or this will leak
  // copy ctor and op= need to make new objects that are copies of baseImpl, dtor needs to delete -- make sure not to share baseImpl between two GridIterator objects
};


template <class T>
class Grid {
   virtual GridIterator<T> begin() = 0;
   virtual GridIterator<T> end() = 0;
};


template <class T>
class GridUniform {

  template <class T>
  class GridUniformIterator : GridIteratorImplBase<T>
      private T* current;
   public:
      GridUniformIterator(T* c) : current(c) {}
      virtual GridIteratorImplBase<T>& operator++() { current++; return *this; }
      virtual T& operator*() { return *current; }
  };

  GridIterator<T> begin() { 
      GridIterator<T> iter(new GridUniformIterator(gridData)); 
      return iter; 
  }
  GridIterator<T> end() { 
      GridIterator<T> iter(new GridUniformIterator(gridData+size));
      return iter; 
  }


  private:
    T* gridData;
    int size;
};

Я набрал это прямо в текстовой области этого ответа - не компилятор. Это должно дать вам идею, чтобы вы могли начать.

  1. начало и конец должны создавать итераторы
  2. Итераторы должны иметь возможность создавать копии и вызывать оператор =. Если вы попытаетесь создать для них один базовый класс, он будет приведен к базе, поэтому вы не сможете использовать для них виртуальный
  3. Чтобы обойти # 2, вы заставляете итераторы просто удерживать указатель на базовый класс реализации итератора.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...