1

Base 型のオブジェクトと、BaseDerivedA および BaseDerivedB の派生型のオブジェクトを格納する必要があります。これらのオブジェクトは、メモリ内で整列する必要があります。すべてのオブジェクトを反復処理する反復子を提供したいと考えています。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 から派生した型を含む 3 つのベクトルを反復処理するより良い方法はありますか?

4

4 に答える 4

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;
};
于 2012-07-23T14:43:26.373 に答える
2

bases最初に、が空の場合、または任意のサイズのderivedBsifend_beginが呼び出された場合、コードは未定義の動作をすることに注意してください。

/チェーンBaseType*の代わりに、単一のコンテナにまたはスマートバリアントを持ち、抽象インターフェイスを使用してそれにアクセスするという、より明白で通常のアプローチができない理由はありますか? その後、問題は完全に解消されます。dynamic_caststatic_cast

insert編集: 何らかの理由で各型のメモリを連続させる必要があり、コンテナーに単一の s を頻繁に実行しない場合はBaseType、派生オブジェクト コンテナー内の各オブジェクトを指すポインターのコンテナーを作成するだけです。しかし、少し戻って、オブジェクトが連続している必要がある理由を確認してください (正当な理由が簡単にある可能性があります)。

于 2012-07-23T14:23:25.997 に答える
1

イテレータが正しいためには、適切に比較できるように、現在トラバースしているベクトルを知る必要があります。これを行うには、どちらが現在のものかを示す列挙を使用します。

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;
}
于 2012-07-23T14:19:55.267 に答える
0

に何があるかが問題になるのはなぜderivedAs.end()ですか? を通じてアクセス/変更することはありませんderivedAs。したがって、その仮定はまったく必要ありません。

典型的なコードは

for(auto it = derivedAs.begin(); it != derivedAs.end(); ++it) {
    *it = // do whatever, will never do *derivedAs.end()
}
于 2012-07-23T14:10:52.720 に答える