2015-06-20 2 views
2

У меня есть чистый абстрактный класс и два производных класса, которые я использую для хранения таких же данных, скажем int, но в разных структурах данных, скажем, map и vector.Пользовательский итератор для нескольких контейнеров в C++

class AbstractContainer { 
    public: 
    virtual MyIterator firstValue() = 0; 
} 

class ContainerMap : public AbstractContainer { 
    private: 
    map<K, int>; 
    public: 
    MyIterator firstValue() { // return iterator over map values (int) } 
} 

class ContainerVector : public AbstractContainer { 
    private: 
    vector<int>; 
    public: 
    MyIterator firstValue() { // return iterator over vector values (int) } 
} 

В ContainerMap я могу подкласс map<K, int>::iterator перебрать значения карты.

Но как я могу определить общий итератор MyIterator, независимо от структуры данных, таким образом, чтобы данный указатель типа AbstractContainer мог перебирать значения, игнорируя фактическую структуру, хранящую данные? И кроме того, это хорошая практика?

Редактировать

Этот вопрос является упрощение задачи. В моем проекте один из подклассов хранит мои объекты в памяти (в std::map), а другой извлекает объекты из внешней базы данных. Я пытаюсь создать общий интерфейс для доступа к коллекции объектов, который не зависит от источника данных, потому что операции (поиск, вставка и удаление) будут точно такими же.

+2

Посмотрите, как это делается в std, с обычными typedefs, определяющими итератор в шаблоне. Конечно, я предполагал, что вы делаете это ради удовольствия. Это УЖАСНАЯ практика. Используйте std для любого реального кода. –

+0

Извините, я не понимаю, почему вы утверждаете, что это ужасная практика. Я использую std для структур данных, когда это возможно ... Я просто хочу, чтобы я обращался к объектам одинаково для разных структур данных. – Marco

+1

Тип Erasure для итераторов - хорошая ссылка для начала. [http://thbecker.net/free_software_utilities/type_erasure_for_cpp_iterators/start_page.html](http://thbecker.net/free_software_utilities/type_erasure_for_cpp_iterators/start_page.html) –

ответ

0

Что бы вы хотели сделать, это создать отдельный класс итератора, который наследует класс итератора стандарта C++. Затем вам нужно будет реализовать все стандартные функции итератора в вашем классе итератора (например, разыменование, ++, ==,! = И т. Д.).

В ваших структурах данных вы хотели бы иметь функцию, которая будет возвращать узел-преемник из любой точки внутри структуры - эта функция будет вызываться перегруженным оператором Итератора ++, чтобы перейти к следующему узлу/value в структуре данных, в том порядке, в котором вы хотите. Например, для вектора, с учетом индекса, вы хотите, чтобы ваш метод-преемник возвращал индекс, который следует за данным индексом в векторе.

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

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

+0

Я не хочу внедрять итератор с нуля, так как это очень опасно. И я не думаю, что это то, что мне нужно, потому что я все еще могу использовать stl-итераторы под капотом. 'ContainerMap' будет использовать' map :: iterator', в то время как 'ContainerVector' будет использовать' vector :: iterator'. Но так как нет общего базового класса для этих двух итераторов, у меня нет типа для объявления в 'AbstractClass' – Marco

1

Ну, нет, это не очень хорошая практика.

Причина, по которой существует более одного типа контейнера (например, в STL), заключается в том, что нет единого контейнера, который оптимизирован для всего. Таким образом, один тип контейнера может быть лучше подходит для использования, когда элементы вставляются в контейнер один раз, и он повторяется несколько раз, а другой контейнер лучше подходит для кода, который должен многократно добавлять и удалять элементы из середины.

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

Это, как и в STL, нет ничего, что останавливало бы два разных контейнера, использующих одно и то же имя для своих итераторов.Таким образом, Container_X и Container_y могут иметь итератор с именем Iterator, но Container_X::Iterator не должен работать так же, как Container_Y::Iterator.

Вы не первый, кто хочет код, который является агностиком контейнера (хотя вы правильно сформулировали его как «агностик для итератора»). И ты не будешь последним. Если какой-либо великий разум не может определить тип контейнера со всеми операциями, оптимальными для всех возможных вариантов использования (в отличие от текущего состояния игры, которое является тем, что каждый тип контейнера является оптимальным для некоторых вариантов использования, но для других - слабым), агностический код контейнера является бесполезная цель. Итератор, который может работать во всех контейнерах, вероятно, будет максимально неэффективным для многочисленных мер для одной или нескольких операций над большинством (если не всех) разных типов контейнеров.

+0

Но мой пользовательский итератор все равно будет использовать' vector :: iterator' и 'map :: iterator' под капотом, каждый оптимизирован для своей структуры данных ... Поэтому я не понимаю, почему это было бы менее эффективно. То, как я думаю, должно работать, заключается в том, что, например, вызов 'operator ++' моего общего итератора вызывал бы' operator ++ 'на специализированном итераторе для используемого контейнера – Marco

+0

Только если' MyIterator' и, следовательно, 'AbstractContainer' реализуется со встроенными знаниями обо всех специализированных контейнерах и их итераторах. Каждый раз, когда вы выносите новый класс из «AbstractContainer», вам нужно будет изменить «MyIterator», чтобы он мог работать с итератором для нового типа контейнера, не нарушая, как он работает с другими итераторами. Это нарушает принцип открытого закрытия (т. Е. Хорошо продуманный код расширяется без необходимости его изменения), и этот принцип является одним из оснований поддерживаемых и многоразовых конструкций или кода. – Peter