web-dev-qa-db-ja.com

独自のイテレーターを作成する

私はC++を学ぼうとしているので、もしこの質問が基本的な知識の欠如を示しているなら、私を許してくれます。

私が作成したクラスのイテレータを作成する方法を理解するための手助けが必要です。

ポイントのコンテナを持つクラス「Shape」があります。 Shapeを参照し、Shapeの位置を定義するクラス「Piece」があります。ピースにはシェイプがなく、シェイプを参照するだけです。

Pieceは、それが参照するShapeのポイントと同じですが、Pieceの位置のオフセットが追加されたPointのコンテナのように見えたいです。

まるでピースがコンテナそのものであるかのように、ピースのポイントを反復処理できるようにしたいと考えています。少し読んでみましたが、私を助けてくれるものは何も見つかりませんでした。どんなポインタにも非常に感謝しています。

139
Howard May

Boost.Iteratorsを使用する必要があります。既存のイテレータ用の新しいイテレータとアダプタを実装するためのテンプレートと概念が多数含まれています。 このまさにトピックに関する記事 ;を書きました。 2008年12月のACCUマガジンに載っています。 Boost.Iteratorsを使用して、オブジェクトからメンバーコレクションを公開する、まさにあなたの問題に対する(IMO)エレガントなソリューションについて説明します。

Stlのみを使用する場合は、 Josuttis book に、独自のSTLイテレーターの実装に関する章があります。

40
Roel

/編集:なるほど、ここでは実際に独自のイテレータが必要です(最初に質問を読み間違えました)。それでも、以下のコードは同じような状況で役立つ可能性があるため、そのままにしておきます。


ここで実際に独自のイテレータが必要ですか?おそらく、必要なすべての定義を実際のポイントを保持しているコンテナに転送するだけで十分です。

// Your class `Piece`
class Piece {
private:
    Shape m_shape;

public:

    typedef std::vector<Point>::iterator iterator;
    typedef std::vector<Point>::const_iterator const_iterator;

    iterator begin() { return m_shape.container.begin(); }

    const_iterator begin() const { return m_shape.container.begin(); }

    iterator end() { return m_shape.container.end(); }

    const_iterator end() const { return m_shape.const_container.end(); }
}

これは、vectorを内部で使用していることを前提としていますが、タイプは簡単に調整できます。

61
Konrad Rudolph

ここ カスタムコンテナのようなSTLの設計 は、コンテナクラスのようなSTLをイテレータクラスと一緒に設計する方法の基本概念のいくつかを説明する優れた記事です。逆反復子(少し厳しい)は演習として残されていますが、:-)

HTH、

20
Abhay

これを読むことができます ddj article

基本的に、std :: iteratorを継承して、ほとんどの作業を完了させます。

15
gbjbaanb

C++でカスタムイテレータを作成することは、理解するのが非常に冗長で複雑になる可能性があります。

カスタムイテレータを作成する最小限の方法を見つけることができなかったので、 このテンプレートヘッダー を記述しました。たとえば、Pieceクラスを反復可能にするには:

#include <iostream>
#include <vector>

#include "iterator_tpl.h"

struct Point {
  int x;
  int y;
  Point() {}
  Point(int x, int y) : x(x), y(y) {}
  Point operator+(Point other) const {
    other.x += x;
    other.y += y;
    return other;
  }
};

struct Shape {
  std::vector<Point> vec;
};

struct Piece {
  Shape& shape;
  Point offset;
  Piece(Shape& shape, int x, int y) : shape(shape), offset(x,y) {}

  struct it_state {
    int pos;
    inline void next(const Piece* ref) { ++pos; }
    inline void begin(const Piece* ref) { pos = 0; }
    inline void end(const Piece* ref) { pos = ref->shape.vec.size(); }
    inline Point get(Piece* ref) { return ref->offset + ref->shape.vec[pos]; }
    inline bool cmp(const it_state& s) const { return pos != s.pos; }
  };
  SETUP_ITERATORS(Piece, Point, it_state);
};

次に、通常のSTLコンテナとして使用できます。

int main() {
  Shape shape;
  shape.vec.emplace_back(1,2);
  shape.vec.emplace_back(2,3);
  shape.vec.emplace_back(3,4);

  Piece piece(shape, 1, 1);

  for (Point p : piece) {
    std::cout << p.x << " " << p.y << std::endl;
    // Output:
    // 2 3
    // 3 4
    // 4 5
  }

  return 0;
}

また、const_iteratorreverse_const_iteratorなど、他のタイプの反復子を追加することもできます。

役に立てば幸いです。

2
VinGarcia

問題の解決策は、独自のイテレーターを作成するのではなく、既存のSTLコンテナーとイテレーターを使用することです。ベクトルのようなコンテナに各形状のポイントを保存します。

class Shape {
    private:
    vector <Point> points;

それから何をするかは、デザイン次第です。最適なアプローチは、Shape内のメソッドのポイントを反復処理することです。

for (vector <Point>::iterator i = points.begin(); i != points.end(); ++i)
    /* ... */

Shapeの外部のポイントにアクセスする必要がある場合(これは設計の欠陥のマークである可能性があります)、Shapeメソッドで作成して、ポイントの反復子アクセス関数を返すことができます(その場合、ポイントコンテナーのパブリックtypedefも作成します)。このアプローチの詳細については、Konrad Rudolphの回答をご覧ください。

1