c++ - 带有子类容器的子类的非标准迭代器

标签 c++ inheritance iterator

抱歉,这太长了,但我想不出如何缩短它并仍然显示我的问题的重要特征。

尽管我已经想出了一个如何做我想做的事情的想法,但我还是在问这个问题,但它需要以一种与标准迭代器模式不匹配的方式实现迭代器。我想就如何以更标准或更好的方式完成这项工作提出任何建议。

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

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 的子类: :iterator 知道如何迭代 MyObjectBase 的子类中的集合。您需要返回对 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() 替换 operator!=(),因为它在这段代码中唯一用于检测迭代器是否在末尾,但是这使它变得非常不标准。

最后,MyObjectA::begin() 和 MyObjectA::end() 不能返回一个临时对象。请记住,我们必须返回指向 MyObjectA::iterator 的 MyObjectBase::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 包含一个 vector ,而 MyImpl 包含一个 vector 迭代器。我想用指向 MyImpl 的 impl_ 实例化一个 Base::Iterator。如果 MyImpl::operator== 采用 MyImpl& 那么它不会重载 Base::Iterator::Impl::operator==,但是如果它采用 Base::Iterator::Impl& 那么我就无法访问 vector 迭代器即使它实际上是一个 MyImpl。

看来我需要进行动态转换才能获得 MyImpl,这需要 RTTI。我听说要尽可能避免使用 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_;
  };
  // ...

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

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

关于c++ - 带有子类容器的子类的非标准迭代器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28952535/

相关文章:

c++ - 这会正确释放数据吗?

scala - 为什么使用 scala 的迭代器实现 crossProduct 会返回错误的结果?

c++ - 用于游戏(和其他任务)的简单而有效的 UDP 服务器策略

c++ - Qt程序不显示

c++ - OpenGL:如何检查用户是否支持 glGenBuffers()?

c++ - 多个基类可以有相同的虚方法吗?

c++ - 试图从二叉搜索树中删除一个节点

c++ - 继承类型强制

javascript - 从 setTimeout() 内部调用 this._super()

php - 是否可以倒回 PDO 结果?