【问题标题】:Iterating over vectors of type Base and/or types derived from base迭代 Base 类型的向量和/或从 base 派生的类型
【发布时间】:2012-07-23 14:01:57
【问题描述】:

我需要存储 Base 类型以及派生类型 BaseDerivedA 和 BaseDeriveddB 的对象。这些对象需要在内存中对齐。我想提供一个迭代所有对象的迭代器。我想避免存储 Base 指针向量的内存开销。

为此,我构建了以下容器

struct Container {
    std::vector<Base> bases;
    std::vector<BaseDerivedA> derivedAs;
    std::vector<BaseDerivedB> derivedBs;

    // Iterator over the three vectors
    all_iterator<Base> all_begin(){ return all_iterator(bases[0],this); }
    all_iterator<Base> end_begin(){ return all_iterator(nullptr,this); }

    // Where all_iterator is defined as
    template < class T >
    struct all_iterator
    : public boost::iterator_facade< all_iterator<T>,
                                     T, boost::forward_traversal_tag>
    {
        all_iterator() : it_(0) {}
        explicit all_iterator(T* p, Container* c) // THIS JUST FEELS WRONG
        : it_(p), c_(c) { }

    private:
        friend class boost::iterator_core_access;
        T* it_;
        Container* c_;
        void increment() {
            if (it_ == static_cast<T*>(&(c_->bases[c_->bases.size()-1]))) {
                it_ = static_cast<T*>(&(c_->derivedAs[0]));
            } else if (it_ == static_cast<T*>(&(c_->derivedAs[ds_->derivedAs.size()-1]))) {
                it_ = static_cast<T*>(&(c_->derivedBs[0]));
            } else if (it_ == static_cast<T*>(&(c_->derivedBs[ds_->derivedBs.size()-1]))) {
                it_ = nullptr; // THIS DOES ALSO FEEL WRONG
            } else {
                ++it_;
            }
        }
        bool equal(all_iterator const& other) const {
            return this->it_ == static_cast<T*>(other.it_);
        }
        T& dereference() const { return *it_; }
    };

我使用 nullptr 作为过去的迭代器以及大量演员表。我还向我的迭代器传递了一个指向数据结构的指针。

有没有更好的方法来迭代三个包含 Base 类型或从 base 派生的类型的向量?

【问题讨论】:

  • 您隐藏了大部分重要代码,例如all_iterator 是什么(它是如何定义的?)以及它如何使用它的参数。您的代码的当前实现(不管all_iterator)表现出未定义的行为:您无法访问derivedBs[derivedBs.size()],因为这是一个超出结尾的元素。
  • “我不能假设derivedBs 末尾的一个不是derivedAs 的第一个元素。” - 这是为什么?考虑到不同容器的迭代器是不可比较的,这是什么意思?
  • 仍然,代码表现出未定义的行为,这意味着所有的赌注都被取消了。该方法不正确,您应该尝试找到解决方案,而不是尝试找到如何使未定义的行为起作用。
  • @DavidRodríguez-dribeas 我已经添加了迭代器的定义:)
  • 我不明白用例。 BaseType 是 DerivedA 和 DerivedB 的基类吗?是否还有另一个类是 BaseType、DerivedA 和 DerivedB 的公共基础?

标签: c++ c++11 iterator


【解决方案1】:

首先我们应该注意,如果bases 为空,或者如果end_begin 被调用,则对于derivedBs 的任何大小,您的代码都有未定义的行为。

您是否有理由不能使用更明显和正常的方法在单个容器中拥有BaseType* 或智能变体,并使用抽象接口来访问它,而不是使用dynamic_cast/@987654326 @ 链?然后问题就完全消失了。

编辑:如果您出于某种原因需要每种类型的内存是连续的,并且您不经常将单个 inserts 放入容器中,只需创建一个指向每个对象的 BaseType 指针容器在派生对象容器内。但我想请你退后一步,回顾一下为什么你需要对象是连续的(很可能有正当的理由)。

【讨论】:

  • 如果有这样的容器,boost::indirect_iterator(来自 Boost.Iterators)风格的东西会很容易实现all_begin/all_end
  • 唯一的原因是我需要在内存中对齐向量的元素。不知道有没有更好的办法。
  • 谢谢! BaseType 指针容器的想法很好!
【解决方案2】:

我假设 BaseType 是 DerivedA 和 DerivedB 的公共基础,并且您希望拥有一个包含 DerivedA 和 DerivedB 实例的容器,并让您能够迭代所有 DerivedA 实例、所有 DerivedB 实例及以上BaseType 的所有实例(即 DerivedA 和 DerivedB 的并集)。你可以这样做:

class BaseType
{
public:
  virtual void doit() const = 0;

  virtual ~BaseType() { }
};

class DerivedA : public BaseType
{
public:
  void doit() const { std::cout << "DerivedA::doit()" << std::endl; }

  void a() const { std::cout << "DerivedA::a()" << std::endl; }
};

class DerivedB : public BaseType
{
public:
  void doit() const { std::cout << "DerivedB::doit()" << std::endl; }

  void b() const { std::cout << "DerivedB::b()" << std::endl; }
};

class Container
{
public:
  void insert(DerivedA const & a)
  {
    m_as.push_back(a);
    m_base.push_back(&m_as.back());
  }

  void insert(DerivedB const & b)
  {
    m_bs.push_back(b);
    m_base.push_back(&m_bs.back());
  }

  std::vector<DerivedA>::iterator begin_a() { return m_as.begin(); }
  std::vector<DerivedA>::iterator end_a() { return m_as.end(); }
  std::vector<DerivedB>::iterator begin_b() { return m_bs.begin(); }
  std::vector<DerivedB>::iterator end_b() { return m_bs.end(); }
  std::vector<BaseType *>::iterator begin_all() { return m_base.begin(); }
  std::vector<BaseType *>::iterator end_all() { return m_base.end(); }

protected:
private:
  std::vector<DerivedA> m_as;
  std::vector<DerivedB> m_bs;
  std::vector<BaseType *> m_base;
};

【讨论】:

  • 谢谢!这是一个不错的选择。我需要检查具有 BaseType 指针向量的内存开销:D
【解决方案3】:

为了使您的迭代器正确,您必须知道当前正在遍历哪个向量,以便正确比较它。您可以通过枚举告诉您哪个是当前的来做到这一点:

void all_iterator::increment()
{
  switch (current_member) {
    case BasesMember:
      ++bases_iter;
      if (bases_iter==bases.end()) {
        current_member = DerivedAsMember;
      }
      return;
    case DerivedAsMember:
      ++derived_as_iter;
      if (derived_as_iter==derivedAs.end()) {
        current_member = DerivedBsMember;
      }
      return;
    case DerivedBsMember:
      ++derived_bs_iter;
      if (derived_bs_iter==derivedBs.end()) {
        current_member = EndMember;
      }
      return;
    case EndMember:
      assert(current_member!=EndMember);
      break;
  }
} 

bool all_iterator::equal(all_iterator const &other) const
{
  if (current_member!=other.current_member) return false;
  switch (current_member) {
    case BasesMember:
      return bases_iter==other.bases_iter;
      break;
    case DerivedAsMember:
      return derived_as_iter==other.derived_as_iter;
      break;
    case DerivedBsMember:
      return derived_bs_iter==other.derived_bs_iter;
      break;
    case EndMember:
      return true
  }
}

Base& all_iterator::dereference() const
{
  switch (current_member) {
    case BasesMember:     return *bases_iter;
    case DerivedAsMember: return *derived_as_iter;
    case DerivedBsMember: return *derived_bs_iter;
    case EndMember:
      assert(current_member!=EndMember);
      break;
  }
  return *bases_iter;
}

【讨论】:

  • 如果向量是 Base 类型或从 base 派生的类型,我不能在比较中直接转换为 base 吗?
  • 不,你实际上需要三个独立的迭代器。
【解决方案4】:

derivedAs.end() 的位置为何重要?您永远不会通过derivedAs 访问/修改它。所以你根本不需要那个假设。

典型的代码是

for(auto it = derivedAs.begin(); it != derivedAs.end(); ++it) {
    *it = // do whatever, will never do *derivedAs.end()
}

【讨论】:

  • derivedBs.end() 中的内容很重要。如果 derivedBs.end() == derivedAs.begin() 循环中断。
  • @gnzlbg 比较从不同容器获得的迭代器是非法的。
  • @ecatmur 出于这个原因,我提供了自己的迭代器。
猜你喜欢
  • 2013-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-24
  • 1970-01-01
  • 2012-12-17
  • 1970-01-01
  • 2013-11-30
相关资源
最近更新 更多