2009-06-29 4 views
9

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

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

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

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

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

ответ

9

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

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

9

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

+0

Если вы возвращаете const_iterators. Затем создайте методы begin() и end() const. –

+0

Yep - Отредактировано, что – Dario

2

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

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

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(); 
} 
2

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

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

std::list<int> DoStuff() 

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

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

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

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

Смежные вопросы