Итерирование и контейнеры умных указателей - PullRequest
0 голосов
/ 13 февраля 2010

У меня есть контейнер умных указателей на изменяемые объекты. Мне нужно написать два цикла for_each , один для доступа к объектам как данные только для чтения, а другой для изменяемых данных. Компилятор говорит мне, что std::vector< boost::shared_ptr<Object> > - это не то же самое, что std::vector< boost::shared_ptr<const Object> >, обратите внимание на const.

Вот мой пример кода:

#include <vector>
#include "boost/shared_ptr.hpp"
#include <iterator>

class Field_Interface
{ ; };
typedef boost::shared_ptr<Field_Interface> Ptr_Field_Interface;
typedef boost::shared_ptr<const Field_Interface> Ptr_Const_Field_Interface;

struct Field_Iterator
  : std::input_iterator<std::forward_iterator_tag, Ptr_Field_Interface>
{
  // forward iterator methods & operators...
};

struct Const_Field_Iterator
  : std::input_iterator<std::forward_iterator_tag, Ptr_Const_Field_Interface>
{
  // forward iterator methods & operators...
};

struct Field_Functor
{
  virtual void operator()(const Ptr_Field_Interface&) = 0;
  virtual void operator()(const Ptr_Const_Field_Interface&) = 0;
};

class Record;
typedef boost::shared_ptr<Record> Ptr_Record;
typedef boost::shared_ptr<const Record> Ptr_Const_Record;

class Record_Base
{
  protected:
    virtual Field_Iterator beginning_field(void) = 0;
    virtual Field_Iterator ending_field(void) = 0;
    virtual Const_Field_Iterator const_beginning_field(void) = 0;
    virtual Const_Field_Iterator const_ending_field(void) = 0;

    void for_each(Field_Functor * p_functor)
    {
       Field_Iterator iter_begin(beginning_field());
       Field_Iterator iter_end(ending_field());
       for (; iter_begin != iter_end; ++ iter_begin)
       {
         (*p_functor)(*iter_begin);
       }
     }
};

class Record_Derived
{
public:
   typedef std::vector<Ptr_Field_Interface> Field_Container;
   typedef std::vector<Ptr_Record>          Record_Container;
private:
   Field_Container m_fields;
   Record_Container m_subrecords;
};

Учитывая все вышеизложенное, как мне реализовать чистые абстрактные методы Record_Base в Record_Derived?

Я пробовал:

  • возвращая m_fields.begin(), что
    возвращает ошибки конвертации (не могу конвертировать std::vector<...> to Field_Iterator)
  • возвращая &m_fields[0], что опасно, потому что предполагает вещи о внутренностях std::vector.

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

Ответы [ 3 ]

1 голос
/ 13 февраля 2010

То, что вы делаете, напоминает шаблоны Composite и Visitor . Эти две модели хорошо сочетаются друг с другом, поэтому кажется, что вы на правильном пути.

Для реализации составного шаблона назначьте следующие роли (см. Диаграмму UML составного шаблона):

  • Лист -> Field
  • Композит -> Record
  • Компонент -> Абстрактный базовый класс Field и Record (не могу придумать хорошее имя)

Операции с компонентами, вызываемые для составных типов, рекурсивно передаются всем дочерним элементам (листьям и другим вложенным составным типам).

Чтобы реализовать шаблон Visitor, перегрузите operator() в ваших классах функторов для каждого подтипа Компонента (Поле и Запись).

Я рекомендую вам получить книгу Design Patterns из книги "Банды четырех", которая лучше объясняет эти концепции и углубляется в детали, чем я могу .

Вот пример кода для поднятия аппетита:

#include <iostream>
#include <vector>
#include "boost/shared_ptr.hpp"
#include "boost/foreach.hpp"

class Field;
class Record;

struct Visitor
{
    virtual void operator()(Field& field) = 0;
    virtual void operator()(Record& field) = 0;
};

class Component
{
public:
    virtual bool isLeaf() const {return true;}
    virtual void accept(Visitor& visitor) = 0;
};
typedef boost::shared_ptr<Component> ComponentPtr;

class Field : public Component
{
public:
    explicit Field(int value) : value_(value) {}
    void accept(Visitor& visitor) {visitor(*this);}
    int value() const {return value_;}

private:
    int value_;
};

class Record : public Component
{
public:
    typedef std::vector<ComponentPtr> Children;
    Record(int id) : id_(id) {}
    int id() const {return id_;}
    Children& children() {return children_;}
    const Children& children() const {return children_;}
    bool isLeaf() const {return false;}
    void accept(Visitor& visitor)
    {
        visitor(*this);
        BOOST_FOREACH(ComponentPtr& child, children_)
        {
            child->accept(visitor);
        }
    }

private:
    int id_;
    Children children_;
};
typedef boost::shared_ptr<Record> RecordPtr;

struct OStreamVisitor : public Visitor
{
    OStreamVisitor(std::ostream& out) : out_(out) {}
    void operator()(Field& field) {out_ << "field(" << field.value() << ") ";}
    void operator()(Record& rec) {out_ << "rec(" << rec.id() << ") ";}
    std::ostream& out_;
};

int main()
{
    RecordPtr rec(new Record(2));
        rec->children().push_back(ComponentPtr(new Field(201)));
        rec->children().push_back(ComponentPtr(new Field(202)));
    RecordPtr root(new Record(1));
        root->children().push_back(ComponentPtr(new Field(101)));
        root->children().push_back(rec);

    OStreamVisitor visitor(std::cout);
    root->accept(visitor);
}

В Record вы можете захотеть предоставить методы для манипулирования / доступа к дочерним элементам вместо возврата ссылки на базовый дочерний вектор.

0 голосов
/ 13 февраля 2010

Если вы хотите скрыть std::vector и его итераторы от пользователя, вам потребуется предоставить полиморфные итераторы, чтобы они соответствовали вашему полиморфному RecordBase контейнеру. Извлеките any_iterator из библиотеки Adobe ASL. Эти ссылки также могут быть полезны.

Однако, вместо того, чтобы идти ко всем этим неприятностям, вам следует рассмотреть возможность использования шаблонов Composite и Visitor в своем дизайне. Смотрите мой другой ответ.

0 голосов
/ 13 февраля 2010

Я бы посоветовал не писать собственный итератор при использовании общего типа контейнера.Написание собственного итератора имеет смысл, когда вы пишете собственный контейнер.Однако, когда вы планируете написать собственный итератор, взгляните на пакет Boost.Iterator .

...