Вопрос

У меня есть контейнер интеллектуальных указателей на изменяемые объекты.Я должен написать два для каждого циклы, один для доступа к объектам как к данным, доступным только для чтения, а другой для изменяемых данных.Компилятор говорит мне, что 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 потому что мне нужно выполнить итерацию по контейнеру полей и контейнер вложенных записей.

Это было полезно?

Решение

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

Чтобы реализовать составной шаблон, назначьте следующие роли (см. UML-диаграмму составного шаблона):

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

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

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

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

Вот несколько примеров кода, которые подогреют ваш аппетит:

#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 вы можете захотеть предоставить методы для манипулирования дочерними элементами / доступа к ним вместо возврата ссылки на базовый дочерний вектор.

Другие советы

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top