智能指针的迭代和容器
-
20-09-2019 - |
题
我有一个指向可变对象的智能指针的容器。我必须写两个 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
因为我必须迭代字段容器 和 子记录的容器。
解决方案
你正在做的事情类似于 合成的 和 游客 模式。这两种模式很好地结合在一起,所以看来您走在正确的轨道上。
要实现复合模式,请分配以下角色(请参阅复合模式 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 中,您可能希望提供操作/访问子项的方法,而不是返回对底层子项向量的引用。
其他提示
我建议当您使用通用容器类型时不要编写自己的迭代器。当您编写自己的容器时,编写自己的迭代器是有意义的。但是,当您计划编写自定义迭代器时,请查看 Boost迭代器 包裹。
如果你想隐藏 std::vector
及其来自用户的迭代器,那么您将需要提供多态迭代器来配合您的多态 RecordBase
容器。查看 any_iterator
来自 Adobe ASL 库。 这些 链接 也可能有帮助。
然而,您应该考虑在设计中使用复合模式和访问者模式,而不是遇到这些麻烦。请参阅我的另一个答案。