具有子类容器的子类的非标准迭代器

nonstandard iterator for subclass with containers of subclasses

本文关键字:子类 非标准 迭代器      更新时间:2023-10-16

>很抱歉这很长,但我不知道如何让它更短,并且仍然显示我问题的重要特征。

即使我已经想出了如何做我想做的事情的想法,我也会问这个问题,但它需要以与标准迭代器模式不匹配的方式实现迭代器。 我想就如何以更标准或更好的方式做到这一点提出任何建议。

我有一个抽象的基类。 它的任何子类都会有一些对象集合。 我想将一些通用代码放在基类中,这些代码需要迭代集合中的所有对象。 集合中的对象都将从另一个抽象基类派生,但将是子类对象。 可能有多个来自 multible 子类的对象集合,并且这些集合可能是不同的集合类型。 我真正想做的是这样的:(注意:此代码无法编译。

class CollectionObjectBase
{
public:
  virtual int someInterface(int x) = 0;
};
class MyObjectBase
{
public:
  class iterator
  {
  public:
    virtual CollectionObjectBase& operator*() = 0;
    virtual iterator& operator++() = 0;
    virtual bool operator!=(iterator& other) = 0;
  };
  virtual iterator begin() = 0;
  virtual iterator end() = 0;
  int processCollections()
  {
    iterator it;
    int      sum = 0;
    for(it = begin(); it != end(); ++it)
      {
        sum += (*it).someInterface(7);
      }
    return sum;
  }
};

然后我想要一些CollectionObjectBase和MyObjectBase的子类,如下所示:

class CollectionObject1 : public CollectionObjectBase { ... }
class CollectionObject2 : public CollectionObjectBase { ... }
class CollectionObject3 : public CollectionObjectBase { ... }
class MyObjectA : public MyObjectBase
{
public:
  std::map<int, CollectionObject1> someThings;
  std::vector<  CollectionObject2> otherThings;
  class iterator : public MyObjectBase::iterator
  {
  public:
    std::map<int, CollectionObject1>::iterator it1;
    std::vector<  CollectionObject2>::iterator it2;
    // Iterate first over it1 and then over it2.
    ...
  };
  virtual MyObjectBase::iterator begin()
  {
    return iterator(someThings.begin(), otherThings.begin());
  }
  virtual MyObjectBase::iterator end()
  {
    return iterator(someThings.end(), otherThings.end());
  }
};
class MyObjectB : public MyObjectBase
{
public:
  std::vector<CollectionObject1> someThings;
  std::set<   CollectionObject3> otherThings;
  class iterator : public MyObjectBase::iterator
  {
  public:
    std::vector<CollectionObject1>::iterator it1;
    std::set<   CollectionObject3>::iterator it2;
    // Iterate first over it1 and then over it2.
    ...
  };
  virtual MyObjectBase::iterator begin()
  {
    return iterator(someThings.begin(), otherThings.begin());
  }
  virtual MyObjectBase::iterator end()
  {
    return iterator(someThings.end(), otherThings.end());
  }
};

上面的代码有一些大问题。 首先,MyObjectBase::begin() 和 MyObjectBase::end() 不能返回 MyObjectBase::iterator ,因为 MyObjectBase::iterator 是一个抽象类,无论如何你都不想这样做,因为你实际上想要一个知道如何迭代 MyObjectBase 子类中的集合的 MyObjectBase::itrator 子类。 您需要返回对 MyObjectBase::iterator 的引用。

第二个问题来自MyObjectA::iterator::operator!=()。 你想做这样的事情:

virtual bool operator!=(iterator& other)
{
  return it1 != other.it1 || it2 != other.it2;
}

但是要成为MyObjectBase::iterator::operator!=()的重载,它必须采用MyObjectBase::iterator&类型的参数,并且没有成员it1和it2。 我想到解决这个问题的最好主意是用函数atEnd()替换运算符!=(),因为在此代码中使用它的唯一用途是检测迭代器是否在末尾,但它使它真正非标准。

最后,MyObjectA::begin() 和 MyObjectA::end() 不能返回临时。 请记住,我们必须返回对 MyObjectBase::iterator 的引用,该引用指向 MyObjectA::iterator,而不是复制构造 MyObjectBase::iterator。 我想到的解决这个问题的最好主意是用new分配迭代器,当我这样做时,我需要返回一个指针,而不是一个引用,以便调用者可以删除它。 这是我的最终代码。 它工作并执行迭代器的功能,但它是非标准的。 谁能想到一种使用符合标准模式的迭代器来做到这一点的方法?

#include <map>
#include <vector>
class CollectionObjectBase
{
public:
  virtual int someInterface(int x) = 0;
};
class MyObjectBase
{
public:
  class iterator
  {
  public:
    virtual CollectionObjectBase& operator*() = 0;
    virtual iterator& operator++() = 0;
    virtual bool atEnd() = 0;
  };
  virtual iterator* begin() = 0;
  int processCollections()
  {
    iterator* it;
    int       sum = 0;
    for (it = begin(); !it->atEnd(); ++it)
      {
        sum += (**it).someInterface(7);
      }
    delete it;
    return sum;
  }
};
class CollectionObject1 : public CollectionObjectBase
{
public:
  virtual int someInterface(int x)
  {
    return x + 1;
  }
};
class CollectionObject2 : public CollectionObjectBase
{
public:
  virtual int someInterface(int x)
  {
    return x + 2;
  }
};
class MyObjectA : public MyObjectBase
{
public:
  std::map<int, CollectionObject1> someThings;
  std::vector<  CollectionObject2> otherThings;
  class iterator : public MyObjectBase::iterator
  {
  public:
    std::map<int, CollectionObject1>::iterator it1;
    std::map<int, CollectionObject1>::iterator it1End;
    std::vector<  CollectionObject2>::iterator it2;
    std::vector<  CollectionObject2>::iterator it2End;
    iterator(std::map<int, CollectionObject1>::iterator it1Init, std::map<int, CollectionObject1>::iterator it1EndInit,
             std::vector<  CollectionObject2>::iterator it2Init, std::vector<  CollectionObject2>::iterator it2EndInit) :
      it1(it1Init),
      it1End(it1EndInit),
      it2(it2Init),
      it2End(it2EndInit)
    {
      // Initialization handled by initialization list.
    }
    virtual CollectionObjectBase& operator*()
    {
      if (it1 != it1End)
        {
          return (*it1).second;
        }
      else
        {
          return *it2;
        }
    }
    virtual iterator& operator++()
    {
      if (it1 != it1End)
        {
          ++it1;
        }
      else
        {
          ++it2;
        }
      return *this;
    }
    virtual bool atEnd()
    {
      return it1 == it1End && it2 == it2End;
    }
  };
  virtual MyObjectBase::iterator* begin()
  {
    return new iterator(someThings.begin(), someThings.end(), otherThings.begin(), otherThings.end());
  }
};

无用的答案有一个问题。 考虑 Impl 和 Base 的这些子类:

class MyImpl : public Base::Iterator::Impl {
public:
  MyImpl(std::vector<CollectionObjectSub>::iterator itInit) : it(itInit) {}
  CollectionObjectBase& operator*() { return *it; }
  void operator++() { ++it; }
//  bool operator==(Base::Iterator::Impl const &other) const { it == other.it; }
  bool operator==(MyImpl const &other) const { it == other.it; }
private:
  std::vector<CollectionObjectSub>::iterator it;
};
class Sub : public Base {
public:
  Iterator& begin() { return Iterator(new MyImpl(collection.begin())); }
private:
  std::vector<CollectionObjectSub> collection;
};

Sub 包含一个向量,MyImpl 包含一个向量迭代器。 我想实例化一个 Base::Iterator,impl_指向 MyImpl。 如果MyImpl::operator==需要一个MyImpl&,那么它不会重载Base::Iterator::Impl::operator==,但如果它需要一个Base::Iterator::Impl&,那么即使它实际上是一个MyImpl,我也无法访问矢量迭代器。

似乎我需要做一个动态演员来获得一个需要RTTI的MyImpl。 我听说如果可能的话要避免使用RTTI。 还有别的办法吗?

因此,您想按值返回迭代器,但其动态类型是否根据您调用的begin/end的覆盖而变化?没问题 - 只需添加另一层间接。

class Base {
public:
  class Iterator {
  public:
    struct Impl {
      virtual ~Impl() {}
      virtual CollectionObjectBase& operator*() = 0;
      virtual void operator++() = 0;
      virtual bool operator==(Iterator::Impl const &other) const = 0;
    };
    Iterator() {}
    explicit Iterator(std::unique_ptr<Impl> &&i) : impl_(std::move(i)) {}
    Iterator(Iterator &&other) = default;
    CollectionObjectBase& operator*() { return **impl_; }
    Iterator& operator++() { ++*impl_; return *this; }
    bool operator==(Iterator const &other) const {
      return (!impl_ && !other.impl_) || (*impl_ == *other.impl_);
    }
  private:
    std::unique_ptr<Impl> impl_;
  };
  // ...

这使用指针指向 impl (pimpl) 习惯用法为每个具体的子类Base提供它自己的具体子类 Base::Iterator::Impl ,但仍然允许您使用移动语义按值传递Base::Iterator对象,以转移动态对象的所有权。

铌。我使两个默认构造的(nullptr)迭代器比较相等,因此您无需为end创建真实实例。