Интерфейс / суперкласс для коллекций / контейнеров на c++

Я прихожу из мира Java и сейчас создаю небольшую программу на c++. У меня есть объект, который выполняет некоторые действия и возвращает результат работы в виде списка.

теперь через день я изменил поведение объекта, чтобы сохранить результаты в наборе, чтобы избежать дублирования в контейнере. Но я не могу просто вернуть набор, потому что я использовал список для интерфейс в первый раз. Есть ли общий интерфейс контейнера, который я могу использовать для указания интерфейса моего объект и забыть о типе контейнера, который я использую внутри?

в данный момент я создаю набор, добавляя все значения, а затем создаю список из набора:

return std::list<foo>(this->mySet.begin(), this->mySet.end())

кажется немного странным.

4 ответов


понятие контейнера enbodied итераторами.
Как вы видели, жесткое кодирование определенного типа контейнера, вероятно, не то, что вы хотите. Поэтому сделайте итераторы возврата класса. Затем вы можете повторно использовать итераторы conatiners.

class MyClass
{
    private:
        typedef  std::list<int>            Container;
    public:
        typedef  Container::iterator       iterator;
        typedef  Container::const_iterator const_iterator; 


        iterator        begin()        {return myData.begin();}
        const_iterator  begin() const  {return myData.begin();}

        iterator        end()          {return myData.end();}
        const_iterator  end()   const  {return myData.end();}

    private:
        Container   myData;
};

теперь, когда вы меняете тип контейнера из std:: list на std:: set, никто не должен знать.
Кроме того, используя стандартные имена, которые используют другие контейнеры, ваш класс начинает выглядеть как любой другой контейнер из STL.

Примечание: метод, который возвращает const_iterator должен быть методом const.


вся стандартная библиотека C++, включая ее контейнеры, - в отличие от Java-не интерфейс (наследование, полиморфизм), а шаблон (ради эффективности).

вы можете создать полиморфную оболочку вокруг вашей коллекции, но это не C++ - путь.

самое простое решение-просто упростить программу с некоторыми псевдонимами типа:

#include <iostream>
#include <list>
#include <vector>

using namespace std;

class Test {

private:
    typedef vector<int> Collection;

    Collection c;

public:

    typedef Collection::const_iterator It;

    void insert(int Item) {
        c.push_back(Item);
    }

    It begin() const { return c.begin(); }
    It end()   const { return c.end(); }

};

int main() {

    Test foo;

    foo.insert(23);
    foo.insert(40);

    for (Test::It i = foo.begin(); i != foo.end(); ++i)
        cout << *i << endl;

    return 0;
}

Теперь вы можете изменить Collection - typedef без необходимости изменять что-либо еще. (Примечание: Если вы Collection public, пользователь сможет ссылаться на тип, который вы использовали явно)


из вашего описания я думаю, что короткий ответ-нет.

В общем, когда я создаю какую-то форму коллекции, я обычно использую typedef для указания контейнера, который я использую:

class Object {
   typedef std::list<int> Cont;
   typedef Cont::iterator iterator;
   typedef Cont::const_iterator const_iterator;

   // ....
};

весь клиентский код ссылается на" Object::Cont " и т. д. и пока клиенты используют только общие функции контейнеров, им не нужно будет меняться, если контейнер изменится.

Если вы не можете изменить свой API сейчас, то я думаю, что ваше решение довольно хорошо, однако, в зависимости от данных, которые у вас есть, если вы делаете много вставок, которые, как правило, уникальны, то может быть более эффективно продолжать использовать список и удалять только дубликаты в конце:

void foo (std::list<int> & list) {

  // ... fill the list

  list.sort ();
  list.unique (); 
}

интерфейс не существует. Вместо этого вы обычно используете шаблоны и просто говорите: "мне все равно, какой это тип, если он ведет себя как контейнер".

предполагая, что ваша функция выглядит так:

std::list<int> DoStuff()

Это можно назвать так:

template <typename container_type>
void caller() {
  container_type result = DoStuff();
}

только первая функция должна быть изменена, если вы решите вернуть set вместо. Вызывающая функция на самом деле не заботится (если вы не полагаетесь на специфику списка, курс.)

Если вы разместите немного больше примеров кода, мы могли бы лучше предложить, как это должно быть сделано на C++.