迭代&容器的智能指针

时间:2010-02-12 23:45:17

标签: c++ inheritance vector iterator shared-ptr

我有一个容易指向可变对象的容器。我必须编写两个 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 个答案:

答案 0 :(得分:1)

您正在做的事情类似于CompositeVisitor模式。这两种模式很好地融合在一起,所以你似乎走在了正确的轨道上。

要实现复合模式,请分配以下角色(请参阅复合模式UML图):

  • Leaf - &gt; Field
  • 复合材料 - &gt; Record
  • 组件 - &gt; FieldRecord的抽象基类(不能想出一个好名字)

在Composite类型上调用的组件操作以递归方式传递给所有子项(Leaves和其他嵌套的Composite类型)。

要实现访问者模式,请在函子类中为每个Component子类型(字段和记录)重载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中,您可能希望提供操作/访问子项的方法,而不是返回对基础子矢量的引用。

答案 1 :(得分:0)

我建议您在使用公共容器类型时不要编写自己的迭代器。编写自己的迭代器在编写自己的容器时很有意义。但是,当您计划编写自定义迭代器时,请查看Boost.Iterator包。

答案 2 :(得分:0)

如果要隐藏用户的std::vector及其迭代器,则需要提供多态迭代器以与多态RecordBase容器一起使用。从Adobe ASL库中查看any_iteratorThese links也可能会有所帮助。

但是,您应考虑在设计中使用Composite和Visitor模式,而不是遇到所有麻烦。看到我的其他答案。