Интерфейс / Суперкласс для Коллекций / Контейнеры в c ++ - PullRequest
10 голосов
/ 29 июня 2009

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

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

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

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

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

Ответы [ 4 ]

10 голосов
/ 29 июня 2009

Понятие контейнера воплощено итераторами.
Как вы уже видели, жесткое кодирование определенного типа контейнера, вероятно, не то, что вам нужно. Поэтому сделайте так, чтобы ваш класс возвращал итераторы. Затем вы можете повторно использовать итераторы 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.

10 голосов
/ 29 июня 2009

Вся стандартная библиотека 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 общедоступным, пользователь сможет ссылаться на тип, который вы использовали явно)

2 голосов
/ 29 июня 2009

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

Предполагается, что ваша функция выглядит следующим образом:

std::list<int> DoStuff()

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

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

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

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

2 голосов
/ 29 июня 2009

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

В общем, когда я создаю некоторую форму коллекции, как это, я обычно использую 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 (); 
}
...