自制迭代器的正确性

Cad*_*hon 11 c++ iterator const-iterator

总目标

我管理(对象的集合CollectionReal简单例子).然后我在我的集​​合上定义了迭代器.这意味着:iterator,const_iterator,reverse_iteratorconst_reverse_iterator.在这个例子中,我只会注意iteratorconst_iterator,其他两个非常相似.

之后,我想在我的集合中定义一个过滤器,它根据特定条件保留或不保留元素.例如,仅保留Real具有正值的实例.我也想在我保留的元素上迭代我的集合.

我是如何实现该集合的

对于此示例,集合中的对象非常简单.目标只是拥有一个对象而不是一个本机类型:

struct Real
{
    public:
      double r;
};
Run Code Online (Sandbox Code Playgroud)

然后我定义我的集合而不必知道里面的真实容器:

class Collection
{
  public:
    typedef std::vector<Real>::iterator iterator;
    typedef std::vector<Real>::const_iterator const_iterator;
  private:
    std::vector<Real> data;
  public:
    Collection() : data() {}
    Collection(unsigned long int n) : data(n) {}
    Collection(unsigned long int n, const Real& x) : data(n,x) {}
    Collection::iterator       begin()       { return this->data.begin(); }
    Collection::iterator       end()         { return this->data.end(); }
    Collection::const_iterator begin() const { return this->data.begin(); }
    Collection::const_iterator end() const   { return this->data.end(); }
};
Run Code Online (Sandbox Code Playgroud)

在这个简单的例子中,这非常有效:

int main()
{
  Collection c(5);
  double k = 1.0;
  for(Collection::iterator it = c.begin(); it != c.end(); ++it)
  {
    it->r = k;
    k *= -2.0;
  }

  std::cout << "print c with Collection::iterator" << std::endl;
  for(Collection::iterator it = c.begin(); it != c.end(); ++it)
    std::cout << it->r << std::endl;

  std::cout << "print c with Collection::const_iterator" << std::endl;
  for(Collection::const_iterator it = c.begin(); it != c.end(); ++it)
    std::cout << it->r << std::endl;

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

此程序写入预期的输出:

print with Collection::iterator
1
-2
4
-8
16
print with Collection::const_iterator
1
-2
4
-8
16
Run Code Online (Sandbox Code Playgroud)

我是如何实现过滤器的

现在我想创建一个抽象过滤器,它具有一个引用或指向集合的指针,具有迭代器,并具有通过过滤器接受值的抽象函数.对于第一步,我只编写没有迭代器的类:

class CollectionFilter
{
  private:
    Collection& col;
  public:
    CollectionFilter(Collection& c) : col(c) {}
    virtual ~CollectionFilter() {}
    Collection& collection() { return this->col; }
    iterator begin() { /* todo */ }
    iterator end() { /* todo */ }
    const_iterator begin() const { /* todo */ }
    const_iterator end() const { /* todo */ }
    virtual bool accept(const Real& x) const = 0;
};
Run Code Online (Sandbox Code Playgroud)

然后,创建一个实现特定条件的新过滤器非常容易:

class CollectionFilterPositive : public CollectionFilter
{
  public:
    CollectionFilterPositive(Collection& c) : CollectionFilter(c) {}
    virtual ~CollectionFilterPositive() {}
    virtual bool accept(const Real& x) const { return x.r >= 0.0; }
};
Run Code Online (Sandbox Code Playgroud)

在过滤器中实现迭代器之前,我有一些评论/问题.

  1. 该过滤器适用于一个非const Collection&的话,是在begin() constend() const功能真正需要的?如果是,为什么?
  2. 我不能在a上应用过滤器const Collection&,但这显然是我的目标所必需的.有什么好办法呢?我是否要将类复制CollectionFilterCollectionFilterConst具有非常相似代码的类?此外,对于必须从两个类似类继承的用户来说,这种解决方案非常混乱.

然后,让我们去实现迭代器.对于这个例子,我只写了iterator而不是const_iterator.我把它添加到我的班级:

class CollectionFilter
{
  public:
    class iterator
    {
      private:
        CollectionFilter*    filter;
        Collection::iterator iter;
      public:
                  iterator(CollectionFilter* f, Collection::iterator i) : filter(f), iter(i) {}
                  iterator(const iterator& i) : filter(i.filter), iter(i.iter) {}
        iterator& operator = (const iterator& i) { this->filter = i.filter; this->iter = i.iter; return *this; }
        iterator& operator ++ ()
        {
          if(this->iter != this->filter->collection().end())
          {
            do
            {
              ++this->iter;
            } while(this->iter != this->filter->collection().end() && !this->filter->accept(*this->iter));
          }
        }
        iterator operator ++ (int) { /* similar */ }
        Real& operator * () { return *this->iter; }
        Collection::iterator operator -> () { return this->iter; }
        bool operator == (const iterator& i) const { return this->iter == i.iter; }
        bool operator != (const iterator& i) const { return this->iter != i.iter; }
    };
  public:
    iterator begin()
    {
      Collection::iterator it = this->col.begin();
      if(!this->accept(*it)) ++it;
      return CollectionFilter::iterator(this,it);
    }
    iterator end()
    {
      Collection::iterator it = this->col.end();
      return CollectionFilter::iterator(this,it);
    }
};
Run Code Online (Sandbox Code Playgroud)

这也适用于这个简单的例子

int main()
{
  Collection c(5);
  double k = 1.0;
  for(Collection::iterator it = c.begin(); it != c.end(); ++it)
  {
    it->r = k;
    k *= -2.0;
  }

  std::cout << "print c with CollectionFilterPositive::iterator" << std::endl;  
  CollectionFilterPositive fc(c);
  for(CollectionFilterPositive::iterator it = fc.begin(); it != fc.end(); ++it)
    std::cout << it->r << std::endl;

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

给出预期的输出:

print with CollectionFilterPositive::iterator
1
4
16
Run Code Online (Sandbox Code Playgroud)

一些问题:

  1. 我对这种方法完全错了吗?
  2. 我想我必须复制代码CollectionFilter::iterator才能实现CollectionFilter::const_iterator,只需要很小的修改.有没有办法避免重复此代码(写入8次,如果我计算重复的类CollectionFilterConst和反向迭代器)?
  3. 我对代码的const正确性感到不舒服.你看到一些问题吗?

提前致谢 !

tml*_*len 1

我建议放弃该类CollectionFilter,而是使用一个Collection::filter_iterator_tmpl模板类,其中有两个实例化Collection::filter_iteratorCollection::const_filter_iterator.

Collection::filter_iterator_tmpl可以这样实现:

class Collection {         
    template<typename Iterator, typename Predicate>
    class filter_iterator_tmpl :
    public std::iterator<std::input_iterator_tag, typename Iterator::value_type, typename Iterator::difference_type, typename Iterator::pointer, typename Iterator::reference> {
    private:
        Iterator underlying_iterator_;
        Predicate predicate_;

    public:
        filter_iterator_tmpl& operator++() {
            do {
                ++ underlying_iterator_;
            } while(! predicate_(*underlying_iterator_));
            return *this;
        }

        typename Iterator::reference operator*() const {
            return *underlying_iterator_;
        }

        ....
    }

};
Run Code Online (Sandbox Code Playgroud)

可以通过让Predicate成为具有函数的多态函数来添加多态性virtual bool PolymorphicPredicate::operator(Real) const

Collection然后定义实际的过滤器迭代器:

class Collection {
private:
    template<typename Iterator, typename Predicate>
    class filter_iterator_tmpl;
public:
    template<typename Predicate>
    using filter_iterator = filter_iterator_tmpl<Collection::iterator, Predicate>;

    template<typename Predicate>
    using const_filter_iterator = filter_iterator_tmpl<Collection::const_iterator, Predicate>;

    template<typename Predicate>
    filter_iterator<Predicate> begin_filter(const Predicate& pred);

    template<typename Predicate>
    const_filter_iterator<Predicate> begin_filter(const Predicate& pred) const;
}
Run Code Online (Sandbox Code Playgroud)

Boost 以类似的方式实现通用的“Filter Iterator”:http://www.boost.org/doc/libs/1_46_1/libs/iterator/doc/filter_iterator.html 作为独立类,而不是容器类的一部分。

关于 const 正确性:C++ 中的容器(std::vectorstd::mapstd::string等)拥有自己的内容对象:它们创建和删除它们,并且需要确保通过对容器的 const 访问,您也只能获得对内容对象的 const 访问。需要实现它们以强制执行此操作,因为它们访问分配的存储的底层指针没有这种所有权概念。这就是为什么他们需要有两个版本的迭代器(iteratorconst_iterator)。迭代器本身并不拥有该对象:通过对 an 进行 const 访问iterator,您无法推进迭代器,但您仍然可以获得对该对象的非常量访问。

问题 1/2: CollectionFilter是有问题的,因为它不拥有它提供访问权限的对象,但对过滤器的常量/非常量访问应该只提供对对象的常量/非常量访问。因为它包含对 , 的引用Collection,并且它应该适用于对Collection, 两个版本的const 和非常量访问CollectionFilterConstCollectionFilter然后需要使用该方法。

问题 4: 一旦将 const 正确的容器对象拆分为用于 const 和非常量访问的两个类,必然会出现一些代码重复。模板避免了手动实现这两个版本。还有一些额外的复杂性,例如比较 aniterator与 an const_iterator,以及const_iterator从 an构造 aiterator但反之则不然......

问题 3/5: 见上文。